Skip to content

Comments

feat: create feehandler system#48

Merged
tac0turtle merged 20 commits intomainfrom
feat/basefee-redirect
Oct 16, 2025
Merged

feat: create feehandler system#48
tac0turtle merged 20 commits intomainfrom
feat/basefee-redirect

Conversation

@tac0turtle
Copy link
Contributor

Description

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Performance improvement
  • Refactoring

Related Issues

Fixes #(issue)

Checklist

  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Testing

Additional Notes

@claude

This comment was marked as resolved.

Copy link
Contributor

@damiannolan damiannolan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will have to review and look into this in much more depth but from what I understand applying changes to the state here outside of the EVM and recomputing the root would be a problem for us in our zk prover, as we depend on RSP which applies transactions using a block builder and computes new roots based on that.

So to me, that would suggest that this implementation would fall outside the scope of that and lead to a divergence of state roots

@jonas089
Copy link
Contributor

jonas089 commented Sep 23, 2025

I will have to review and look into this in much more depth but from what I understand applying changes to the state here outside of the EVM and recomputing the root would be a problem for us in our zk prover, as we depend on RSP which applies transactions using a block builder and computes new roots based on that.

So to me, that would suggest that this implementation would fall outside the scope of that and lead to a divergence of state roots

I share this concern. Manually mutating the state root rather than taking the resulting state root from the execution of the EVM transactions could make it impossible to replay the transactions in ZK, unless all of the operations can be re-applied and the same state root can be derived.

Also I wonder about the implications this has for eth_getProof?

Edit: Or is the original state root still exposed and used for eth_getProof?

@tac0turtle
Copy link
Contributor Author

still looking into how to change this without manipulating state outside the evm level

Copy link

@liamsi liamsi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some nitpicks / typos while reading through the code

Copy link
Contributor

@damiannolan damiannolan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not review the ev-revm code in detail. I don't have intimate knowledge of evm internals but given the ADR and my higher level understanding this all computes and makes sense to me.

I think this approach leveraging reward_beneficiary() is much better than the previous, first drafted design and integrates better overall with the prover arch we have been working with.

We will have to allocate time to dedicate to testing this once its merged and ev-reth is happy with it.

Is it possible to get a git tag cut with the ev-reth code before this PR is merged? If its just an alpha or beta tag that's totally fine.

Comment on lines 27 to 28
/// EVM configuration (potentially wrapped with base fee redirect)
pub evm_config: WrappedEthEvmConfig,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make more sense to call this type alias EvolveEvmConfig ?

Suggested change
/// EVM configuration (potentially wrapped with base fee redirect)
pub evm_config: WrappedEthEvmConfig,
/// EVM configuration (with optional base fee redirect)
pub evm_config: EvolveEvmConfig,

"terminalTotalDifficulty": 0,
"terminalTotalDifficultyPassed": true
"terminalTotalDifficultyPassed": true,
"ev_reth": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, all configuration fields are camelCase in these genesis files and this is snake_case.

Maybe we could consider something else, e.g. just evolve

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ADR is helpful, thank you! 🙏🏻

So this just deals strictly with the base fee, correct? The "tip" still goes to a potentially different or potentially the same coinbase/beneficiary still?

total_fee = gas_price * gas_used
base_fee  = block.baseFeePerGas * gas_used
tip       = total_fee - base_fee

base fee -> burn || reward_beneficiary(baseFeeSink)
tip -> coinbase/beneficiary

use tracing::debug;
use tracing::{debug, info};

type WrappedEthEvmConfig = EthEvmConfig<ChainSpec, EvEvmFactory<EthEvmFactory>>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is what we need in order to operate succinct/rsp. As this should implement the following trait: https://github.com/paradigmxyz/reth/blob/main/crates/evm/evm/src/lib.rs#L184

From what I've looked at this is what matters - as this has the full set of API calls wired with the essential overrides. It would require a definition similar to this but with the EvEvmFactory wired in: https://github.com/succinctlabs/rsp/blob/main/crates/executor/client/src/executor.rs#L151-L161

We will have to test from our side but this seems to be the integration point I was looking for.

let gas = exec_result.gas();
let spent = gas.spent_sub_refunded();

if let (Some(redirect), true) = (self.redirect, spent != 0) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we log sth if redirect is None or spent is 0? Or is the expectation that there will always be gas spent or a redirect in place? either way, I think it would be good to improve the introspecatibility here.


/// Validates the configuration
pub const fn validate(&self) -> Result<(), ConfigError> {
Ok(())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure the extra added sink can't be invalid in some way (like an invalid address)? Shouldn't this be checked here?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this further: if setting the addr to 0xffffffffffffffffffffffffffffffffffffffff or other special addresses this might defeat the purpose. I think we should do further validation on the sink addr.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would it defeat the purpose? setting it to a predefined special address should be fine

Copy link

@liamsi liamsi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a few issues with factory.rs when trying to get this PR to compile and run tests locally. I think this should be fixed before approving this PR. I left a few other rather non-blocking comments. The design makes sense to me. The code base is new to me though and I can't really say much about prover compatibility but if @damiannolan says this works for the prover part, that's good.

@damiannolan
Copy link
Contributor

Once this branch is compiling and we can run an ev-reth instance with it using the base fee redirect then I'm happy to allocate time to testing it with the prover based on what I mentioned in #48 (comment)

Copy link

@liamsi liamsi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a few more tests and this can be merged.

self.inner.reimburse_caller(evm, exec_result)
}

fn reward_beneficiary(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we would add a test that this method behaves as expected in the happy and unhappy paths or rather that the integration works correctly. I see the tests in for redirect.apply in base_fee.rs but would it be possible to test the redirect itself without too much hassle? (Like some minimal Context::mainnet() with EmptyDB or sth?) Please ignore if this is too complex. As mentioned I'm not too familiar with the code base.

## Test Cases

* Unit tests cover the redirect logic inside `BaseFeeRedirect` and verify the handler credits both the sink and block beneficiary as expected (`crates/ev-revm/src/base_fee.rs`, `crates/ev-revm/src/factory.rs`).
* Integration smoke tests should execute payload-building runs with and without the `config.ev_reth.baseFeeSink` field present to confirm state roots differ only when the redirect is active.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand the tests in common.rs correctly, the config and the resulting fixture there does not use base_fee_sink. I think it would be good to add this param there too.


We introduced a new `ev-revm` crate that mirrors Optimism’s `op-revm` layout but adds an `EvEvm` wrapper. The wrapper:

* Stores the optional `BaseFeeRedirect` policy and a flag indicating whether inspection is active.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question regarding this flag:

if self.inspect {
InspectEvm::inspect_tx(self, tx)
} else {
ExecuteEvm::transact(self, tx)
}

The redirection of fees works in both cases, right?

}

let amount = U256::from(base_fee) * U256::from(gas_used);
if amount.is_zero() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this even be zero after the check above?

if gas_used == 0 || base_fee == 0 {
      return Ok(U256::ZERO);
}

i, gas_used
);
}
Err(err) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is a dumb question but do we need to make sure that the sink is also credited here? I would assume this is not necessary as this code existed before and did not require a particular burn for fees instead.

.load_account(self.fee_sink)
.map_err(BaseFeeRedirectError::Database)?;
journal
.balance_incr(self.fee_sink, amount)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: What is the expected behaviour if the sender is equal to the sink? (Just trying to think of any edge cases).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be a free transaction as the fee would just be moved from the users wallet back to the users wallet


/// Validates the configuration
pub const fn validate(&self) -> Result<(), ConfigError> {
Ok(())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this further: if setting the addr to 0xffffffffffffffffffffffffffffffffffffffff or other special addresses this might defeat the purpose. I think we should do further validation on the sink addr.

@tac0turtle tac0turtle marked this pull request as ready for review October 14, 2025 13:52
@tac0turtle tac0turtle requested a review from a team as a code owner October 14, 2025 13:52
@tac0turtle
Copy link
Contributor Author

marking it for r4r tested locally, ill address the issues and try to identify possible issues

Copy link

@liamsi liamsi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

@tac0turtle tac0turtle merged commit f176ff1 into main Oct 16, 2025
14 checks passed
@tac0turtle tac0turtle deleted the feat/basefee-redirect branch October 16, 2025 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants