Skip to content

Commit 064180f

Browse files
pdobaczmarioevz
authored andcommitted
new(tests): EIP-7702 - ensure DELEGATECALLing a 7702 target works (ethereum#1485)
Co-authored-by: Mario Vega <[email protected]>
1 parent 1a43361 commit 064180f

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Test fixtures for use by clients are available for each release on the [Github r
1818

1919
### 🧪 Test Cases
2020

21+
-[EIP-7702](https://eips.ethereum.org/EIPS/eip-7702): Test that DELEGATECALL to a 7702 target works as intended ([#1485](https://github.com/ethereum/execution-spec-tests/pull/1485)).
2122
-[EIP-2573](https://eips.ethereum.org/EIPS/eip-2537): Includes a BLS12 point generator, alongside additional coverage many of the precompiles ([#1350](https://github.com/ethereum/execution-spec-tests/pull/1350)).
2223

2324
## [v4.3.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v4.3.0) - 2025-04-18
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
"""Test related to making calls to accounts having a delegation set on them."""
2+
3+
import itertools
4+
from enum import Enum, auto, unique
5+
6+
import pytest
7+
8+
from ethereum_test_tools import (
9+
EOA,
10+
Account,
11+
Address,
12+
Alloc,
13+
Environment,
14+
StateTestFiller,
15+
Transaction,
16+
)
17+
from ethereum_test_tools.vm.opcode import Opcodes as Op
18+
19+
pytestmark = pytest.mark.valid_from("Prague")
20+
REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7702.md"
21+
REFERENCE_SPEC_VERSION = "37ce65b354756ac51c6a6f114e15de4a523a7f2e"
22+
23+
LEGACY_CALL_FAILURE = 0
24+
LEGACY_CALL_SUCCESS = 1
25+
26+
"""Storage addresses for common testing fields"""
27+
_slot = itertools.count(1)
28+
slot_code_worked = next(_slot)
29+
slot_call_result = next(_slot)
30+
slot_returndata = next(_slot)
31+
slot_last_slot = next(_slot)
32+
33+
"""Storage values for common testing fields"""
34+
value_code_worked = 0x2015
35+
36+
37+
identity = Address(0x04)
38+
39+
40+
@unique
41+
class TargetAccountType(Enum):
42+
"""Kinds of target accounts for calls."""
43+
44+
EMPTY = auto()
45+
EOA = auto()
46+
LEGACY_CONTRACT = auto()
47+
LEGACY_CONTRACT_INVALID = auto()
48+
LEGACY_CONTRACT_REVERT = auto()
49+
IDENTITY_PRECOMPILE = auto()
50+
51+
def __str__(self) -> str:
52+
"""Return string representation of the enum."""
53+
return f"{self.name}"
54+
55+
56+
@pytest.fixture
57+
def sender(pre: Alloc) -> EOA:
58+
"""Sender of the transaction."""
59+
return pre.fund_eoa()
60+
61+
62+
@pytest.fixture
63+
def target_address(pre: Alloc, target_account_type: TargetAccountType) -> Address:
64+
"""Target address of the call depending on required type of account."""
65+
match target_account_type:
66+
case TargetAccountType.EMPTY:
67+
return pre.fund_eoa(amount=0)
68+
case TargetAccountType.EOA:
69+
return pre.fund_eoa()
70+
case TargetAccountType.LEGACY_CONTRACT:
71+
return pre.deploy_contract(
72+
code=Op.STOP,
73+
)
74+
case TargetAccountType.LEGACY_CONTRACT_INVALID:
75+
return pre.deploy_contract(
76+
code=Op.INVALID,
77+
)
78+
case TargetAccountType.LEGACY_CONTRACT_REVERT:
79+
return pre.deploy_contract(
80+
code=Op.REVERT(0, 0),
81+
)
82+
case TargetAccountType.IDENTITY_PRECOMPILE:
83+
return identity
84+
85+
86+
@pytest.mark.parametrize("target_account_type", TargetAccountType)
87+
@pytest.mark.parametrize("delegate", [True, False])
88+
@pytest.mark.parametrize("call_from_initcode", [True, False])
89+
def test_delegate_call_targets(
90+
state_test: StateTestFiller,
91+
pre: Alloc,
92+
target_account_type: TargetAccountType,
93+
target_address: Address,
94+
delegate: bool,
95+
call_from_initcode: bool,
96+
):
97+
"""
98+
Test contracts doing delegatecall to various targets resolved via 7702
99+
delegation.
100+
"""
101+
env = Environment()
102+
103+
if delegate:
104+
target_address = pre.fund_eoa(0, delegation=target_address)
105+
106+
delegate_call_code = Op.SSTORE(
107+
slot_call_result, Op.DELEGATECALL(address=target_address)
108+
) + Op.SSTORE(slot_code_worked, value_code_worked)
109+
110+
if call_from_initcode:
111+
# Call from initcode
112+
caller_contract = delegate_call_code + Op.RETURN(0, 0)
113+
tx = Transaction(
114+
sender=pre.fund_eoa(),
115+
to=None,
116+
data=caller_contract,
117+
gas_limit=4_000_000,
118+
)
119+
calling_contract_address = tx.created_contract
120+
else:
121+
# Normal call from existing contract
122+
caller_contract = delegate_call_code + Op.STOP
123+
calling_contract_address = pre.deploy_contract(caller_contract)
124+
125+
tx = Transaction(
126+
sender=pre.fund_eoa(),
127+
to=calling_contract_address,
128+
gas_limit=4_000_000,
129+
)
130+
131+
calling_storage = {
132+
slot_code_worked: value_code_worked,
133+
slot_call_result: LEGACY_CALL_FAILURE
134+
if target_account_type
135+
in [TargetAccountType.LEGACY_CONTRACT_INVALID, TargetAccountType.LEGACY_CONTRACT_REVERT]
136+
else LEGACY_CALL_SUCCESS,
137+
}
138+
139+
post = {
140+
calling_contract_address: Account(storage=calling_storage),
141+
}
142+
143+
state_test(
144+
env=env,
145+
pre=pre,
146+
post=post,
147+
tx=tx,
148+
)

whitelist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ call
758758
callcode
759759
return
760760
delegatecall
761+
delegatecalling
761762
eofcreate
762763
eoftest
763764
extcall

0 commit comments

Comments
 (0)