Skip to content

Add support for EIP-7702 #323

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 27 commits into
base: v0.x
Choose a base branch
from

Conversation

manuelmauro
Copy link

Description

Adds EIP-7702 support.

@manuelmauro manuelmauro marked this pull request as ready for review June 18, 2025 15:36
Copy link

@librelois librelois left a comment

Choose a reason for hiding this comment

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

It seems that there is several special cases that might not be handled correctly in this implementation:

  1. Delegation Chain Protection: EIP-7702 specifies that clients must retrieve only the first code and stop following delegation chains to prevent infinite loops.
  2. Delegation to a Precompile: EIP-7702 specifies that when a precompile address is the target of delegation, the retrieved code should be considered empty.
  3. Opcodes CODESIZE and CODECOPY should follows delegation, can you add tests for that?

@sorpaas
Copy link
Member

sorpaas commented Jun 23, 2025

Delegation Chain Protection: EIP-7702 specifies that clients must retrieve only the first code and stop following delegation chains to prevent infinite loops.

See discussions above.

Delegation to a Precompile: EIP-7702 specifies that when a precompile address is the target of delegation, the retrieved code should be considered empty.

This seems to be correctly implemented. In case of delegating to precompile:

  • The initial precompile check will not be triggered because it checks against the authority address.
  • Then it would fetch delegated_code, and in case of precompile, it's empty.

So this seems fine.

Opcodes CODESIZE and CODECOPY should follows delegation, can you add tests for that?

Right now from my review it looks correct. CODESIZE and CODECOPY returns in all cases the runtime code (state.code).

But nevertheless as @librelois has said, it'll always be better to have some more test cases!

@manuelmauro
Copy link
Author

If address is 0x0000000000000000000000000000000000000000, do not write the delegation indicator. Clear the account’s code by resetting the account’s code hash to the empty code hash 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470.

Addressed in 3134553

@manuelmauro
Copy link
Author

Added a comprehensive test suite in f5cc851 and currently investigating the failing tests.

@manuelmauro
Copy link
Author

EIP-7702 states: "When multiple tuples from the same authority are present, set the code using the address in the last valid occurrence."

It is my understanding that, when multiple authorizations from the same authorities are present in a list, for them to be valid they must have increasing nonces.

I updated the corresponding test to reflect this in cc32e59


// Should either succeed (infinite loop prevented) or fail gracefully
assert!(
matches!(call_exit_reason, ExitReason::Succeed(_))

Choose a reason for hiding this comment

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

It should fail as the EOA can't have valid executable bytecode

@librelois
Copy link

You tested EXTCODECOPY and EXTCODESIZE, but I didn’t find tests for the corresponding behavior of CODECOPY and CODESIZE. It’s very important to test all affected opcodes.

@librelois
Copy link

librelois commented Jun 24, 2025

It is my understanding that, when multiple authorizations from the same authorities are present in a list, for them to be valid they must have increasing nonces.

I did some research and it seems that geth increment the nonce for every valid tuple. So, if there is two authorizations for the same authority, the nonce must increase twice.

Comment on lines +2522 to +2540
let metadata = evm::executor::stack::StackSubstateMetadata::new(1000000, &config);
let state = evm::executor::stack::MemoryStackState::new(metadata, &mut backend);
let precompiles = BTreeMap::new();
let mut executor = StackExecutor::new_with_precompiles(state, &config, &precompiles);

let authorization =
create_authorization(U256::from(1), implementation, U256::zero(), authorizing);

// Provide insufficient gas (less than the 25000 needed for authorization)
let (exit_reason, _) = executor.transact_call(
caller,
target,
U256::zero(),
Vec::new(),
20_000, // Insufficient gas
Vec::new(),
vec![authorization],
);

Copy link
Author

Choose a reason for hiding this comment

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

This test fails because the gas recording validation compares against the gasometer's internal gas_limit field instead of the gas_limit parameter passed to executor.transact_call.

This behavior may be intentional - I'm not familiar enough with the codebase to determine the design intent. However, the current implementation appears to ignore the passed gas limit parameter entirely, allowing the test transaction to succeed when it might be expected to fail.

Choose a reason for hiding this comment

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

It’s expected that in the EVM there are two gas limits: one for the transaction and one for each subcall.

The proper way to use the EVM is to create a new Executor instance for each transaction, then call transact_call for the root call and for every subcall. For example, in Frontier we create a new Executor instance at the start of each Ethereum transaction here: https://github.com/moonbeam-foundation/frontier/blob/moonbeam-polkadot-stable2412/frame/evm/src/runner/stack.rs#L296

You should refactor your tests so that the gas limit is placed in the executor’s metadata state, and ensure you create a fresh Executor instance for each transaction.

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.

3 participants