Skip to content

Commit ac2ecf3

Browse files
Add delegation contract verification for EIP-7702 accounts (#57)
1 parent a23171c commit ac2ecf3

File tree

7 files changed

+34
-34
lines changed

7 files changed

+34
-34
lines changed

eip7702-core/src/constants.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
use alloy::primitives::{Address, address};
2-
3-
// The minimal account implementation address used for EIP-7702 delegation
4-
// pub const MINIMAL_ACCOUNT_IMPLEMENTATION_ADDRESS: Address =
5-
// address!("0xD6999651Fc0964B9c6B444307a0ab20534a66560");
6-
// NOTE!: do not hardcode. If needed later, use tw_getDelegationContract
7-
81
/// EIP-7702 delegation prefix bytes
92
pub const EIP_7702_DELEGATION_PREFIX: [u8; 3] = [0xef, 0x01, 0x00];
103

eip7702-core/src/delegated_account.rs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl<C: Chain> DelegatedAccount<C> {
2828
}
2929

3030
/// Check if the EOA has EIP-7702 delegation to the minimal account implementation
31-
pub async fn is_minimal_account(&self) -> Result<bool, EngineError> {
31+
pub async fn is_minimal_account(&self, delegation_contract: Option<Address>) -> Result<bool, EngineError> {
3232
// Get the bytecode at the EOA address using eth_getCode
3333
let code = self
3434
.chain
@@ -60,25 +60,28 @@ impl<C: Chain> DelegatedAccount<C> {
6060
// Extract the target address from bytes 3-23 (20 bytes for address)
6161
// EIP-7702 format: 0xef0100 + 20 bytes address
6262

63-
// NOTE!: skip the actual delegated target address check for now
64-
// extremely unlikely that an EOA being used with engine is delegated to a non-minimal account
65-
// Potential source for fringe edge cases, please verify delegated target address if debugging 7702 execution issues
66-
67-
// let target_bytes = &code[3..23];
68-
// let target_address = Address::from_slice(target_bytes);
69-
70-
// // Compare with the minimal account implementation address
71-
// let is_delegated = target_address == MINIMAL_ACCOUNT_IMPLEMENTATION_ADDRESS;
63+
let target_bytes = &code[3..23];
64+
let target_address = Address::from_slice(target_bytes);
65+
66+
// Compare with the minimal account implementation address
67+
let is_delegated = match delegation_contract {
68+
Some(delegation_contract) => {
69+
target_address == delegation_contract
70+
}
71+
None => {
72+
true
73+
}
74+
};
7275

73-
// tracing::debug!(
74-
// eoa_address = ?self.eoa_address,
75-
// target_address = ?target_address,
76-
// minimal_account_address = ?MINIMAL_ACCOUNT_IMPLEMENTATION_ADDRESS,
77-
// has_delegation = is_delegated,
78-
// "EIP-7702 delegation check result"
79-
// );
76+
tracing::debug!(
77+
eoa_address = ?self.eoa_address,
78+
target_address = ?target_address,
79+
minimal_account_address = ?delegation_contract,
80+
has_delegation = is_delegated,
81+
"EIP-7702 delegation check result"
82+
);
8083

81-
Ok(true)
84+
Ok(is_delegated)
8285
}
8386

8487
/// Get the EOA address

eip7702-core/src/transaction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ impl<C: Chain> MinimalAccountTransaction<C> {
195195
credentials: &SigningCredential,
196196
delegation_contract: Address,
197197
) -> Result<Self, EngineError> {
198-
if self.account.is_minimal_account().await? {
198+
if self.account.is_minimal_account(Some(delegation_contract)).await? {
199199
return Ok(self);
200200
}
201201

eip7702-core/tests/integration_tests.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,7 @@ impl TestSetup {
657657
async fn test_eip7702_integration() -> Result<(), Box<dyn std::error::Error>> {
658658
// Set up test environment
659659
let mut setup = TestSetup::new().await?;
660+
let delegation_contract = setup.delegation_contract.expect("Delegation contract should be set");
660661

661662
// Step 1: Fetch and set bytecode from Base Sepolia
662663
setup.fetch_and_set_bytecode().await?;
@@ -666,11 +667,11 @@ async fn test_eip7702_integration() -> Result<(), Box<dyn std::error::Error>> {
666667

667668
// Step 3: Test is_minimal_account - all should be false initially
668669
assert!(
669-
!developer_account.is_minimal_account().await?,
670+
!developer_account.is_minimal_account(Some(delegation_contract)).await?,
670671
"Developer should not be minimal account initially"
671672
);
672673
assert!(
673-
!user_account.is_minimal_account().await?,
674+
!user_account.is_minimal_account(Some(delegation_contract)).await?,
674675
"User should not be minimal account initially"
675676
);
676677
println!("✓ All accounts are not minimal accounts initially");
@@ -731,7 +732,7 @@ async fn test_eip7702_integration() -> Result<(), Box<dyn std::error::Error>> {
731732
);
732733

733734
assert!(
734-
developer_account.is_minimal_account().await?,
735+
developer_account.is_minimal_account(Some(delegation_contract)).await?,
735736
"Developer should be minimal account after minting"
736737
);
737738

@@ -751,14 +752,14 @@ async fn test_eip7702_integration() -> Result<(), Box<dyn std::error::Error>> {
751752
.await?;
752753

753754
assert!(
754-
user_account.is_minimal_account().await?,
755+
user_account.is_minimal_account(Some(delegation_contract)).await?,
755756
"User (session key granter) should be minimal account after delegation"
756757
);
757758
println!("✓ User (session key granter) is now a minimal account (delegated by executor)");
758759

759760
// Step 9: Developer is already delegated via add_authorization_if_needed in owner_transaction
760761
assert!(
761-
developer_account.is_minimal_account().await?,
762+
developer_account.is_minimal_account(Some(delegation_contract)).await?,
762763
"Developer (session key grantee) should already be minimal account from earlier delegation"
763764
);
764765
println!("✓ Developer (session key grantee) was already delegated in previous step");

executors/src/eoa/authorization_cache.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use moka::future::Cache;
99
pub struct AuthorizationCacheKey {
1010
eoa_address: Address,
1111
chain_id: u64,
12+
delegation_contract: Address,
1213
}
1314

1415
#[derive(Clone)]
@@ -24,14 +25,16 @@ impl EoaAuthorizationCache {
2425
pub async fn is_minimal_account<C: Chain>(
2526
&self,
2627
delegated_account: &DelegatedAccount<C>,
28+
delegation_contract: Option<Address>,
2729
) -> Result<bool, EngineError> {
2830
self.inner
2931
.try_get_with(
3032
AuthorizationCacheKey {
3133
eoa_address: delegated_account.eoa_address,
3234
chain_id: delegated_account.chain.chain_id(),
35+
delegation_contract: delegation_contract.unwrap_or(Address::ZERO),
3336
},
34-
delegated_account.is_minimal_account(),
37+
delegated_account.is_minimal_account(delegation_contract),
3538
)
3639
.await
3740
.map_err(|e| e.deref().clone())

executors/src/eoa/worker/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ where
172172
// if there's an error checking 7702 delegation here, we'll just assume it's not a minimal account for the purposes of max in flight
173173
let is_minimal_account = self
174174
.authorization_cache
175-
.is_minimal_account(&delegated_account)
175+
.is_minimal_account(&delegated_account, None)
176176
.await
177177
.inspect_err(|e| {
178178
tracing::error!(error = ?e, "Error checking 7702 delegation");

server/src/execution_router/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ impl ExecutionRouter {
405405
let delegated_account = DelegatedAccount::new(eoa_execution_options.from, chain);
406406
let is_minimal_account = self
407407
.authorization_cache
408-
.is_minimal_account(&delegated_account)
408+
.is_minimal_account(&delegated_account, None)
409409
.await
410410
.map_err(|e| EngineError::InternalError {
411411
message: format!("Failed to check 7702 delegation: {e:?}"),

0 commit comments

Comments
 (0)