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 16 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
Comment on lines +36 to +41
/// Get code of address, following EIP-7702 delegations if enabled.
fn delegated_code(&self, address: H160) -> Option<Vec<u8>> {
let code = self.code(address);
evm_core::extract_delegation_address(&code)
.map(|delegated_address| self.code(delegated_address))
}

Choose a reason for hiding this comment

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

It seems that your implementation provide no protection against delegation chains/loops.

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

You might need to implement delegation chain tracking to prevent infinite loops.

Copy link
Author

Choose a reason for hiding this comment

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

The delegated code is resolved once within call_inner, then executed with no further check.

// EIP-7702: Get execution code, following delegations if enabled
		let code = if self.config.has_eip_7702 {
			self.delegated_code(code_address)
				.unwrap_or_else(|| self.code(code_address))
		} else {
			self.code(code_address)
		};

Under the assumption that call_inner is not invoked within a loop, this should be enough to prevent chain following in my mind.

I added a test to check the behavior of the current implementation: 2564062

The result:

cargo test test_eip7702_delegation_chain_violation -- --nocapture
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.04s
     Running unittests src/lib.rs (target/debug/deps/evm-e7728aa468c7f7c2)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/eip7702_tests.rs (target/debug/deps/eip7702_tests-ff89983bca9ad4d2)

running 1 test
Exit reason: Error(InvalidCode(Opcode(239)))
Return data: []
✓ CORRECT: Delegation chain properly stopped, invalid code executed
test test_eip7702_delegation_chain_violation ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 17 filtered out; finished in 0.00s

Maybe there are other execution branches where this could be violated?
Are there some conservative hardening measures that we could adopt?

Copy link
Member

Choose a reason for hiding this comment

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

It looks sufficient. From my understanding of the specification, we need to prevent loops per call. So each call attempts to fetch delegation exactly once, which seems to be what this PR is doing.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe there are other execution branches where this could be violated?

From my look at the code there's only a single use of delegated_code so it seems fine.

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!

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