Skip to content

Conversation

@Wizdave97
Copy link
Contributor

@Wizdave97 Wizdave97 commented Jan 20, 2026

Description

This PR implements EIP-7702: Set EOA Account Code for pallet-revive. This implementation allows Externally Owned Accounts (EOAs) to temporarily delegate their execution to smart contract code via signed authorizations.

Key Components Implemented

1. Core Authorization Processing (src/evm/eip7702.rs)

New Module: Complete implementation of authorization tuple processing

  • process_authorizations(): Main function to process authorization lists
  • process_single_authorization(): Validates individual authorization tuples
  • recover_authority(): Recovers signer address using ECDSA signature recovery
  • authorization_intrinsic_gas(): Calculates gas costs for authorizations

Key Features:

  • Signature validation with EIP-2 compliance (normalized s-values)
  • Chain ID validation (0 = any chain, or must match current chain)
  • Nonce verification
  • Authority recovery using keccak256(MAGIC || rlp([chain_id, address, nonce]))
  • Last-valid-wins logic for multiple authorizations from same authority

2. Delegation Indicator Storage (src/storage.rs)

Extended AccountInfo with EIP-7702-specific methods:

// Check if code is a delegation indicator
pub fn is_delegation_indicator(code: &[u8]) -> bool

// Extract target address from delegation indicator
pub fn extract_delegation_target(code: &[u8]) -> Option<H160>

// Check if account has delegation set
pub fn is_delegated(address: &H160) -> bool

// Get delegation target
pub fn get_delegation_target(address: &H160) -> Option<H160>

// Set delegation indicator (0xef0100 || address)
pub fn set_delegation(address: &H160, target: H160, nonce: T::Nonce) -> Result<(), DispatchError>

// Clear delegation, restore to EOA
pub fn clear_delegation(address: &H160) -> Result<(), DispatchError>

Storage Strategy:

  • Delegation indicators stored directly in PristineCode without CodeInfo
  • No refcounting/ownership tracking needed
  • Format: 0xef0100 || <20-byte address> (23 bytes total)

3. Code Execution Resolution (src/exec.rs)

New Function: resolve_delegation(code_hash: H256) -> H256

  • Checks if code is a delegation indicator
  • Resolves to target contract's code hash
  • Only follows one level of delegation (prevents loops per EIP-7702)
  • Returns EMPTY_CODE_HASH if target is not a contract
  • Integrated into:
    • new_frame() for contract calls
    • Delegate calls

Behavior:

  • CALL, CALLCODE, DELEGATECALL, STATICCALL follow delegation
  • EXTCODEHASH, EXTCODESIZE, EXTCODECOPY operate on delegation indicator itself
  • During delegated execution: CODESIZEEXTCODESIZE (per spec)

4. Transaction Integration (src/lib.rs, src/evm/call.rs)

Modified Dispatchables:

pub fn eth_call(
    // ... existing parameters ...
    authorization_list: Vec<evm::AuthorizationListEntry>,
) -> DispatchResultWithPostInfo

Note: authorization_list is only added to eth_call, not to eth_instantiate_with_code. EIP-7702 is for setting code on existing EOAs, not for contract creation.

Processing Flow:

  1. Authorization list processed (new step)
  2. Delegation indicators set/cleared
  3. Increment account nonce
  4. Contract execution proceeds

New Helper Function:

pub fn process_eip7702_authorizations(
    authorization_list: Vec<evm::AuthorizationListEntry>,
    accessed_addresses: &mut alloc::collections::BTreeSet<H160>,
) -> u64  // Returns gas refund

6. EIP-3607 Modification (src/lib.rs)

Updated ensure_non_contract_if_signed():

// Allow EOAs with delegation indicators to originate transactions
if AccountInfo::<T>::is_contract(&address) {
    if AccountInfo::<T>::is_delegated(&address) {
        return Ok(())  // ✅ Allow delegated EOAs
    }
    return Err(DispatchError::BadOrigin)  // ❌ Reject regular contracts
}

// ❌ Reject precompiles
if exec::is_precompile::<T, ContractBlob<T>>(&address) {
    return Err(DispatchError::BadOrigin)
}

Per EIP-7702: EOAs with valid delegation indicators can originate transactions.

@seadanda seadanda requested review from athei and pgherveou January 21, 2026 08:57
@pgherveou
Copy link
Contributor

/bot fmt

Copy link
Contributor

@pgherveou pgherveou left a comment

Choose a reason for hiding this comment

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

can you remove the pure fmt diff, it will make it easier to review

@Wizdave97
Copy link
Contributor Author

can you remove the pure fmt diff, it will make it easier to review

Done

Copy link
Contributor

@pgherveou pgherveou left a comment

Choose a reason for hiding this comment

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

Hey, thanks for putting this together.
I think the implementation still has a few gaps; I’ve added some initial remarks.

@Wizdave97 Wizdave97 requested a review from xermicus January 23, 2026 16:23
@pgherveou
Copy link
Contributor

pallet-revive-fixtures gets stuck while building

it requires some bin like solc & resolc I believe, the build error should tell you why it fails I presume

@Wizdave97
Copy link
Contributor Author

pallet-revive-fixtures gets stuck while building

it requires some bin like solc & resolc I believe, the build error should tell you why it fails I presume

It doesn't fail, it just gets stuck on building and never progresses.

I'll try again, maybe I wasn't patient enough

@Wizdave97
Copy link
Contributor Author

pallet-revive-fixtures gets stuck while building

it requires some bin like solc & resolc I believe, the build error should tell you why it fails I presume

It doesn't fail, it just gets stuck on building and never progresses.

I'll try again, maybe I wasn't patient enough

Gets stuck at this point for more than 20 minutes

image

@Wizdave97
Copy link
Contributor Author

@pgherveou I have now added actual benchmarks

@Wizdave97
Copy link
Contributor Author

@pgherveou @athei, just a warm reminder about this issue.

@pgherveou
Copy link
Contributor

@pgherveou @athei, just a warm reminder about this issue.

nice sorry for the delay will take another look at it today

@pgherveou
Copy link
Contributor

Took a quick look, this looks better already.
The benchmark part still needs some work, you still need to charge different weight if the account already exists or not,
because you need to fetch state for that it can't just go in the weight attribute

@pgherveou
Copy link
Contributor

also please can we remove the AI generated comments that just describe what the next line is doing

@Wizdave97
Copy link
Contributor Author

Took a quick look, this looks better already. The benchmark part still needs some work, you still need to charge different weight if the account already exists or not, because you need to fetch state for that it can't just go in the weight attribute

Can you describe how to go about this benchmark better? I'm guessing I'll have to convert the benchmark weight to evm gas, then adjust the supplied gas limit?

@pgherveou
Copy link
Contributor

Took a quick look, this looks better already. The benchmark part still needs some work, you still need to charge different weight if the account already exists or not, because you need to fetch state for that it can't just go in the weight attribute

Can you describe how to go about this benchmark better? I'm guessing I'll have to convert the benchmark weight to evm gas, then adjust the supplied gas limit?

might be overlooking some details but I would do the following:
create a benchmark for the first pass,,and second pass
first pass should also probably tell us if the accounts to process are new or not
then we can charge both weights from the generated benchmark functions

@Wizdave97
Copy link
Contributor Author

Took a quick look, this looks better already. The benchmark part still needs some work, you still need to charge different weight if the account already exists or not, because you need to fetch state for that it can't just go in the weight attribute

Can you describe how to go about this benchmark better? I'm guessing I'll have to convert the benchmark weight to evm gas, then adjust the supplied gas limit?

might be overlooking some details but I would do the following: create a benchmark for the first pass,,and second pass first pass should also probably tell us if the accounts to process are new or not then we can charge both weights from the generated benchmark functions

I'll work on this

@Wizdave97
Copy link
Contributor Author

@pgherveou I've updated the benchmarks to be more dynamic. I'm not 100% sure if my method of converting weight to Ethereum gas is correct, but please have a look again.

@Wizdave97 Wizdave97 requested a review from pgherveou January 29, 2026 12:41
@pgherveou
Copy link
Contributor

@pgherveou I've updated the benchmarks to be more dynamic. I'm not 100% sure if my method of converting weight to Ethereum gas is correct, but please have a look again.

Thanks will make some changes and a PR that target your branch

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