image source head

Ethereum is about to usher in Pectra upgrade: In-depth discussion of EIP-7702 and best practices

trendx logo

Reprinted from panewslab

03/27/2025·1M

Author: Kong

Editor: Sherry

Preface

Ethereum is about to usher in a Pectra upgrade, which is undoubtedly a significant update, and many important Ethereum improvement proposals will be introduced in this opportunity. Among them, EIP-7702 has transformed Ethereum External Account (EOA). The proposal blurs the line between EOA and contract account CA, and is a key step towards native account abstraction following EIP-4337, bringing a new interactive mode to the Ethereum ecosystem.

At present, Pectra has completed its deployment on the test network and is expected to be launched on the main network soon. This article will conduct in-depth analysis of the implementation mechanism of EIP-7702, explore the opportunities and challenges it may bring, and provide practical operational guides for different participants.

Protocol Analysis

Overview

EIP-7702 introduces a completely new transaction type that allows EOA to specify a smart contract address and then set up code for it. In this way, EOA can execute code like a smart contract, while retaining the ability to initiate transactions. This feature gives EOA programmability and composability, so that users can realize functions such as social recovery, permission control, multi-signal management, zk verification, subscription payment, transaction sponsorship, and transaction batch processing in EOA. It is worth mentioning that EIP-7702 can be perfectly compatible with the smart contract wallet implemented by EIP-4337. The seamless integration of the two greatly simplifies the development and application process of new functions.

The specific implementation of EIP-7702 is to introduce transactions with transaction type SET_CODE_TX_TYPE (0x04), and its data structure is defined as follows:

rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, value, data, access_list, authorization_list, signature_y_parity, signature_r, signature_s])

Where the authorization_list field is defined as:

authorization_list = [[chain_id, address, nonce, y_parity, r, s], ...]

In the new transaction structure, except for the authorization_list field, the rest follows the same semantics as EIP-4844. This field is a list type, and the list can contain multiple authorization entries, in each authorization entry:

  • The chain_id field indicates the chain in which this authorization delegation takes effect
  • The address field represents the target address of the delegation
  • The nonce field must match the nonce of the currently authorized account
  • The y_parity, r, s fields are the authorized signature data signed by the authorized account.

The authorization_list field in a transaction can contain authorization entries signed by multiple different authorization accounts (EOAs), that is, the transaction initiator can be different from the authorization to implement gas payment for the authorization operation of the authorization.

accomplish

When the authorized person signs the authorized data, he or she needs to RLP encoding chain_id, address, and nonce first. The encoded data is then keccak256 hashing operation with the MAGIC number to obtain the data to be signed [1]. Finally, the hashed data is signed using the authorized private key, and then the y_parity, r, s data is obtained. Among them, MAGIC (0x05) is used as a domain delimiter, and its purpose is to ensure that the results of different types of signatures do not conflict.

// Go-ethereum/core/types/tx_setcode.go#L109-L113func (a *SetCodeAuthorization) sigHash() common.Hash { return prefixedRlpHash(0x05, []any{ a.ChainID, a.Address, a.Nonce, })}

It should be noted that when the chain_id authorized by the authorized authorization is 0, it means that the authorized authorization allows [2] to replay authorization on all EVM-compatible chains that support EIP-7702 (provided that nonce also just matches).

// Go-ethereum/core/state_transition.go#L562if !auth.ChainID.IsZero() && auth.ChainID.CmpBig(st.evm.ChainConfig().ChainID) != 0 { return authority, ErrAuthorizationWrongChainID}

When the authorized data is signed, the transaction initiator will gather it in the authorization_list field for signature and broadcast the transaction through RPC. Before the transaction is included in the block to execute, the Proposer will pre-check the transaction [3], where the to address is mandatory to ensure that the transaction is not a contract-created transaction, that is, when sending a transaction of type EIP-7702, the to address of the transaction cannot be empty [4].

// Go-ethereum/core/state_transition.go#L388-L390if msg.To == nil { return fmt.Errorf("%w (sender %v)", ErrSetCodeTxCreate, msg.From)}

At the same time, such transactions will force the authorization_list field in the transaction to contain at least one authorization entry. If multiple authorization entries are signed by the same authorizer, then only the last authorization entry will take effect in the end.

// Go-ethereum/core/state_transition.go#L391-L393if len(msg.SetCodeAuthorizations) == 0 { return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From)}

Subsequently, during the transaction execution process, the node will first increase the nonce value of the transaction initiator, and then applyAuthorization operation for each authorization entry in the authorization_list. In the applyAuthorization operation, the node will first check the author's nonce, and then add the author's nonce. This means that if the transaction initiator and authorized are the same user (EOA), the value of nonce should be added to 1 when signing the authorized transaction.

// Go-ethereum/core/state_transition.go#L489-L497func (st *stateTransition) execute() (*ExecutionResult, error) { ... st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall)

// Apply EIP-7702 authorizations. if msg.SetCodeAuthorizations != nil { for _, auth := range msg.SetCodeAuthorizations { // Note errors are ignored, we simply skip invalid authorizations here. st.applyAuthorization(&auth) } } ...}// Go-ethereum/core/state_transition.go#L604func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization) error { authority, err := st.validateAuthorization(auth) ... st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization) ...}// Go-ethereum/core/state_transition.go#L566func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorization) (authority common.Address, err error) { ... if auth.Nonce+1 < auth.Nonce { return authority, ErrAuthorizationNonceOverflow } ...}

When a node applies an authorization entry, if any error is encountered, the authorization entry will be skipped and the transaction will not fail. Other authorization entries will continue to be applied to ensure that there will be no DoS risk in the batch authorization scenario.

// Go-ethereum/core/state_transition.go#L494for _, auth := range msg.SetCodeAuthorizations { // Note errors are ignored, we simply skip invalid authorizations here. st.applyAuthorization(&auth)}

After the authorization application is completed, the code field of the authorization address will be set to 0xef0100 || address, where 0xef0100 is a fixed identity and address is the delegated destination address. Due to EIP-3541 limitations, users cannot deploy contract codes starting with 0xef bytes in conventional ways, which ensures that such identification can only be deployed by transactions of type SET_CODE_TX_TYPE (0x04).

// Go-ethereum/core/state_transition.go#L612st.state.SetCode(authority, types.AddressToDelegation(auth.Address))

// Go-ethereum/core/types/tx_setcode.go#L45var DelegationPrefix = []byte{0xef, 0x01, 0x00}

func AddressToDelegation(addr common.Address) []byte { return append(DelegationPrefix, addr.Bytes()...)}

After the authorization is completed, if the authorized person wants to remove the authorization, he only needs to set the delegate's target address to 0 address.

The new transaction type introduced through EIP-7702 allows authorizers (EOAs) to execute code like smart contracts, while retaining the ability to initiate transactions. Compared with EIP-4337, this brings users a closer experience to native account abstraction (Native AA), greatly reducing the user's threshold for use.

Best Practices

Although EIP-7702 has injected new vitality into the Ethereum ecosystem, new application scenarios will also bring new risks. The following are the aspects that eco-participants need to be vigilant during their practice:

Private key storage

Even if EOA can use the built-in social recovery and other means of smart contracts to solve the problem of fund losses caused by private key loss after delegation, it still cannot avoid the risk of EOA's private key being leaked. It should be clear that after the entrustment is executed, the EOA private key still has the highest control over the account, and holding the private key can dispose of assets in the account at will. After the user or wallet service provider completes the delegation for EOA, even if the private key stored locally is completely deleted, the risk of private key leakage cannot be completely eliminated, especially in scenarios where supply chain attack risks exist.

For users, when using the delegated account, users should still put the protection of their private keys first. Always pay attention: Not your keys, not your coins.

Multi-chain replay

When a user signs a delegation authorization, he can select the chain in which the delegation can take effect through chainId. Of course, the user can also choose to use chainId to delegate with 0, which allows the delegation to be replayed and effective on multiple chains, so that the user can delegate on multiple chains with one signature. But it should be noted that different implementation codes may also exist in the same contract address delegated on multiple chains.

For wallet service providers, when the user delegates, they should check whether the delegation effective chain matches the currently connected network, and remind the user to sign a delegation with chainId of 0.

Users should also note that the contract codes of the same contract addresses on different chains are not always the same, and they should first understand the goal of the delegation.

Unable to initialize

Most of the mainstream smart contract wallets currently use the proxy model. When deploying the wallet agent, it will use DELEGATECALL to realize the initialization function of the contract, so as to achieve the atomization operation of wallet initialization and proxy wallet deployment, and avoid the problem of being pre-initialized. However, when a user uses EIP-7702 to delegate, he will only update the code field of his address and cannot be initialized by calling the delegate address. This makes EIP-7702 unable to call the initialization function in the transactions deployed by the contract for wallet initialization like the common ERC-1967 proxy contract.

For developers, when combining EIP-7702 with existing EIP-4337 wallets, they should pay attention to performing permission checks in the initialization operation of the wallet (for example, permission checks through the recovery of signature addresses through erecover) to avoid the risk of wallet initialization operation being taken off.

Storage Management

When users use the EIP-7702 delegation function, they may need to re-enter to different contract addresses due to changes in function requirements, wallet upgrades, etc. However, the storage structure of different contracts may differ (for example, slot0 slots of different contracts may represent different types of data). In the case of re-entrusting, it may lead to the new contract accidentally reuse of the data of the old contract, which in turn leads to adverse consequences such as account locking and capital losses.

For users, the re-entrusted situation should be handled with caution.

For developers, the Namespace Formula proposed by ERC-7201 should be followed during the development process, and variables should be assigned to a specified independent storage location to mitigate the risk of storage conflicts. In addition, ERC-7779 (draft) provides a standard process for re-delegating EIP-7702: including using ERC-7201 to prevent storage conflicts, verifying storage compatibility before re-delegating, and calling the interface of the old delegation to clean up stored old data.

Fake recharge

After the user entrusts, EOA will also be available as a smart contract, so centralized exchanges (CEXs) may face the generalization of smart contract recharges.

CEX should check the status of each recharge transaction through trace to prevent the risk of fake recharge of smart contracts.

Account conversion

After the EIP-7702 delegation is implemented, the user's account type can be freely converted between EOA and SC, which allows the account to initiate transactions or be called. Meaning that when the account calls itself and makes an external call, its msg.sender will also be tx.origin, which will break some security assumptions that only EOA participates in projects.

For contract developers, assuming that tx.origin is always EOA will no longer be feasible. Similarly, defending against reentrant attacks through msg.sender == tx.origin check will also fail.

During the development process, developers should assume that future participants may all be smart contracts.

Contract Compatibility

The existing ERC-721 and ERC-777 tokens have a Hook function when transferring money to the contract, which means that the receiver must implement the corresponding callback function to successfully receive the token.

For developers, the target contract delegated by the user should implement the corresponding callback function to ensure compatibility with mainstream tokens.

Fishing inspection

After the EIP-7702 delegation is implemented, the assets in the user's account may be controlled by the smart contract. Once the user delegates the account to a malicious contract, it will be easy for an attacker to steal funds.

For wallet service providers, it is particularly important to support EIP-7702 type transactions as soon as possible, and when users sign their delegation, the target contracts of the delegation should be displayed to the user to mitigate the risk of the user's possible phishing attacks.

In addition, more in-depth automatic analysis of the target contracts delegated by the account (open source checks, permission checks, etc.) can better help users avoid such risks.

Summarize

This article discusses the EIP-7702 proposal in Ethereum's upcoming Pectra upgrade. EIP-7702 blurs the boundaries between EOA and contract accounts by introducing new transaction types. Since there is currently no smart contract standard compatible with EIP-7702 type that has been tested in practice, in actual applications, different ecological participants, such as users, wallet service providers, developers, CEXs, etc., are facing many challenges and opportunities. The best practices explained in this article cannot cover all potential risks, but they are still worth learning from and applying in actual operations.

Example

[Set EOA Account Code]

https://holesky.etherscan.io/tx/0x29252bf527155a29fc0df3a2eb7f5259564f5ee7a15792ba4e2ca59318080182

[Unset EOA Account Code]

https://holesky.etherscan.io/tx/0xd410d2d2a2ad19dc82a19435faa9c19279fa5b96985988daad5d40d1a8ee2269

Related links

[1] https://github.com/ethereum/go-ethereum/blob/7fed9584b5426be5db6d7b0198acdec6515d9c81/core/types/tx_setcode.go#L109-L113

[2] https://github.com/ethereum/go-ethereum/blob/7fed9584b5426be5db6d7b0198acdec6515d9c81/core/state_transition.go#L562

[3] https://github.com/ethereum/go-ethereum/blob/7fed9584b5426be5db6d7b0198acdec6515d9c81/core/state_transition.go#L304

[4] https://github.com/ethereum/go-ethereum/blob/7fed9584b5426be5db6d7b0198acdec6515d9c81/core/state_transition.go#L388-L390

References‍‍‍‍‍

[EIP-7702]https://eips.ethereum.org/EIPS/eip-7702

[EIP-4844]https://eips.ethereum.org/EIPS/eip-4844

[Go-ethereum]https://github.com/ethereum/go-ethereum/tree/7fed9584b5426be5db6d7b0198acdec6515d9c81

[EIP-3541]https://eips.ethereum.org/EIPS/eip-3541#backwards-compatibility

[Cobo: EIP-7702 Practical Guide]

https://mp.weixin.qq.com/s/ojh9uLw-sJNArQe-U73lHQ

[Viem]https://viem.sh/experimental/eip7702/signAuthorization

[ERC-7210]https://eips.ethereum.org/EIPS/eip-7201

[ERC-7779]https://eips.ethereum.org/EIPS/eip-7779

more