EIP-7702 Introduction
2025-03-21 09:32
Taipei Ethereum Meetup
2025-03-21 09:32
订阅此专栏
收藏此文章

understand what is EIP-7702 and why is EIP-7702 in Pectra :)

Special thanks to NIC Lin and Chang-Wu Chen for the feedback and review.

tl;dr

  • EIP-7702 is included in the Pectra hardfork in 2025 (this year).
  • EIP-7702 can convert an EOA into a Smart Contract Account; with 7702, your EOA could act like a contract.
  • Private key should still be self-custodied.
  • EIP-7702 introduces a new type of transaction (0x04).

Preface

This article aims to help people understand the basic concept behind this proposal.

Some example code would be included in this article, mainly focusing on the exp-0001, an experiment project that ithaca did in EIP-7702.

So first, what is EIP-7702?? And what kind of problem does it want to solve?

pic from @0xNairolf

One of the goal of Ethereum is to abstract current two kind of accounts:

  1. Externally Owned Account (EOA): an EOA can init a transaction yet cannot execute code.
  2. Smart Contract Account (SCA): A smart contract is able to execute code, yet cannot init a transaction.

EIP-7702 act as a bridge between EOA and smart contract, which enable an EOA to act like a smart contract.

For example, when an EOA interact with a smart contract looks like:

After EIP-7702 set up, an interaction would be like:

Okay, but what is the problem with EOA?

Single point of failure: EOAs are controlled by only one private key and lack of mechanism for recovery or multi-sigature protection.

Limited Functionality: EOAs can only send transactions but not execute code. This is bad since it cannot:

  • Batch transaction
  • Custom Authentications
  • Built in recovery options

Gas Fee Payment: EOAs can only pay the gas in ETH.

All in all, current EOA has a worse UX design. And thus several account abstraction ideas came out.

History regarding AA

Account Abstractions had been discussed for years. And there a quite a few Account Abstraction solution appeared from 2021–2024.

(2021) EIP-4337

  • = UserOps (mempool) + EntryContract + Bundler + Payment master.
  • This is a smart-contract-level design

(2020~2024) EIP-3074

  • = Invoker + AUTH + AUTHCALL
  • Stagnant; replaced by EIP-7702

(2024) EIP-7702

  • Set Code Transaction + Delegation Designator
  • This is a protocol-level design

Native AA in Ethereum

One of the motivations of EIP-7702 is that current (before Pectra Hardfork) account design. This EIP blurred the line between an EOA or a smart contract, and this is also the end goal that the Ethereum want to achieve — RIP-7560 (aka the native account abstraction that supports from the consensus layer).

What and How does it do?

  1. EIP-7702 allows an EOA to execute smart contract code during a transaction by specifying a delegation designator.
  2. This designator points to the smart contract code after “set code transaction”. The code field will remain the same until the EOA cancel authorization (adding address(0) in the authorization_list within another set code tx)

My imagination of the delegation designator is Sutando in Jojo.

While executing transactions through delegation designator be like:

More Details in EIP-7702

Currently we have 4 slots in an EOA in Ethereum:

  1. nonce
  2. balance
  3. code
  4. storage root

The code in the EOA account is empty; that is, there's no code value storing in an EOA account. EIP-7702 introduces a new way to set code field by sending a new type of EIP-2178 transaction, "Set Code Transaction".

The transaction code is different from a normal transaction from EIP-1559 (tx type = 0x02), it has to send more data in the transaction payload.

In, this tx, an EOA has to sign the message with these additional four infomation:

  • chain_id: The chain on which the transaction will take place
  • address: The smart contract address that the EOA wants to point to
  • nonce: Security protection for replay purposes
  • y_parity (== v), r and s: Components of the signature that are used to recover the authority (the signer also the EOA) (y_parity is actually v they are the same)

Now the transaction payload looks like:

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 looks like:

authorization_list = [
[chain_id_1, address_1, nonce_1, y_parity_1, r_1, s_1],
[chain_id_2, address_2, nonce_2, y_parity_2, r_2, s_2],
...
]

After signing, the transaction is generated with the authorization List included. What this actually means is: “As a EOA, you authorized/allows some code to live in your account.”

However, at the moment, EIP-3607 stands out and say: "No, I'm not gonna allow you to do so", since if you have some code in your account, it is impossible for you to originate a transaction.

Here EIP-7702 introduce another method to make it work, which is “Delegation Designator”. What is done in the authorization is that the code is actually set into a target address like this:

0xef + 0100 + target address

By doing so, EIP-3607 is satisfied, and what's even better, the EOA doesn't have to store the whole bytecode of its contract; we can just use a deployed contract to convert your EOA into a Smart Contract Account!

* EIP-3541 prohibit contract address to have “0xef” prefix. The “0xef” is set to be a EOF(=Ethereum Object Format) in Etheruem.
* The original design is to store contract bytecode into the code field; This is refined to only store a short address pointing to the contract.
* authorization_list is a list of tuples that store the address to code which the signer desires to execute in the context of their EOA.
* with chain_id = 0, you can set up delegation designator to all EIP-7702 chains.

Some examples of EIP-7702

It would be hard to understand WHY do we need EIP-7702 without some examples. With EIP-7702, we could do:

  • Multicall to Approve & “Other Functions”: The Delegation Designator is able to implement batch call in the contract, and execute multiple actions in just one transaction.
  • Batch Transfer: Now you are able to send 100 token A, 52 token B, 2 token C, respectively to Alice, Bob, and Charlie.
  • Delegation to ERC-4337: EIP-7702 is backward and forward compatiable to EIP-4337. Your EOA can delegate the code to a deployed ERC-4337 Account Abstraction implementation.
  • Using different signature to authorize transaction: instead of using the native secp256k1 ECDSA on Etheruem, we could set up signatures like secp256r1 to authorize the accounts.

more examples like:

pic from jchaskin22

All of these sounds pretty good and would give the users a better UX! But how should we actually leverage EIP-7702?

Implementation of EIP-7702

It is also hard to understand it without implementation :D. So I also read some code in ithaca odyssey, a L2 that already included EIP-7702 in their execution layer. They also have a well-written blog and you can give it a look.

In this section, we’ll walk through a simplified version of the EIP-7702 implementation. We’ll explain how an EOA can delegate code execution to a smart contract, how authorization works, and how batch transactions are executed.

In exp-0001, ExperimentDelegation.sol is used as the Delegation Designator. And P256 and WebAuthenP256 publicKey (curve: secp256r1) are stored in the contract.

This means: You can use a passkey (i.e., FaceId, TouchId) to sign a transaction without your private key.

Here’s a simplified version of it.

contract ExperimentDelegation is MultiSendCallOnly {
////////////////////////////////////////////////////////////////////////
// Data Structures
////////////////////////////////////////////////////////////////////////

/// @notice A Key that can be used to authorize calls.
/// @custom:property authorized - Whether the key is authorized.
/// @custom:property publicKey - ECDSA public key.
/// @custom:property expiry - Unix timestamp at which the key expires.
struct Key {
bool authorized;
uint256 expiry;
ECDSA.PublicKey publicKey;
}
// ...
////////////////////////////////////////////////////////////////////////
// Functions
////////////////////////////////////////////////////////////////////////
/// @notice List of keys associated with the Authority.
Key[] public keys;
/// @notice Authorizes a new public key on behalf of the Authority, provided the Authority's signature.
/// @param publicKey - The public key to authorize.
/// @param expiry - The Unix timestamp at which the key expires.
/// @param signature - EOA secp256k1 signature over the public key.
function authorize(
ECDSA.PublicKey calldata publicKey,
uint256 expiry,
ECDSA.RecoveredSignature calldata signature
) public returns (uint32 keyIndex) {
bytes32 digest = keccak256(
abi.encodePacked(nonce++, publicKey.x, publicKey.y, expiry)
);
address signer = ecrecover(
digest,
signature.yParity == 0 ? 27 : 28,
bytes32(signature.r),
bytes32(signature.s)
);
if (signer != address(this)) revert InvalidSignature();
Key memory key = Key({
authorized: true,
expiry: expiry,
publicKey: publicKey
});
keys.push(key);
return uint32(keys.length - 1);
}
// ...
/// @notice Executes a set of calls on behalf of the Authority, provided a WebAuthn-wrapped P256 signature over the calls, the WebAuthn metadata, and an invoker index.
/// @param calls - The calls to execute.
/// @param signature - The WebAuthn-wrapped P256 signature over the calls: `p256.sign(keccak256(nonce ‖ calls))`.
/// @param metadata - The WebAuthn metadata.
/// @param keyIndex - The index of the authorized public key to use.
function execute(
bytes memory calls,
ECDSA.Signature memory signature,
WebAuthnP256.Metadata memory metadata,
uint32 keyIndex
) public {
bytes32 challenge = keccak256(abi.encodePacked(nonce++, calls));
Key memory key = keys[keyIndex];
if (!key.authorized) revert KeyNotAuthorized();
if (key.expiry > 0 && key.expiry < block.timestamp) revert KeyExpired();
if (!WebAuthnP256.verify(challenge, metadata, signature, key.publicKey))
revert InvalidSignature();
multiSend(calls);
}
}

Two primary two functions are used in the contract:

authorize :

  • authorize function lets an EOA associate a new public key (passkey) with the delegation contract.

execute :

  • execute function enables the batch execution of multiple calls.
  • It also verifies the WebAuthn-wrapped P256 (also P256) signature.
  • it uses multiSend to execute the batched calls.

Signing an Authorization

So first, we need to sign the message of this SetCodeTransaction. With viem::signAuthorization function, we can simply send a walletClient and sign the transaction. This message indicates that your EOA approves the delegation of code execution to the designated contract:

import { walletClient } from './client'

const authorization = await walletClient.signAuthorization({
contractAddress: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // the delegation designator
})

// where the authorization_list looks like:
// [{
// chainId: 1,
// contractAddress: "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
// nonce: 1,
// r: "0xf507fb8fa33ffd05a7f26c980bbb8271aa113affc8f192feba87abe26549bda1",
// s: "0x1b2687608968ecb67230bbf7944199560fa2b3cffe9cc2b1c024e1c8f86a9e08",
// yParity: 0,
// }]

const hash = await walletClient.sendTransaction({
authorizationList: [authorization],
data: '0xdeadbeef',
to: walletClient.account.address,
})

However, signing the message is not enough. You still have to send a transaction on chain to set up the code for your EOA.

Authorize and Execute

Once the authorization is signed and the delegation code should be injected into your EOA.

// Sign an EIP-7702 authorization to inject the ExperimentDelegation contract
// onto the EOA.
const authorization = await signAuthorization(client, {
account,
contractAddress: ExperimentDelegation.address,
delegate: true,
})

// Send an EIP-7702 contract write to authorize the WebAuthn key on the EOA.
const hash = await writeContract(client, {
abi: ExperimentDelegation.abi,
address: account.address,
functionName: 'authorize',
args: [
{
x: publicKey.x,
y: publicKey.y,
},
expiry,
{
r: BigInt(signature.r),
s: BigInt(signature.s),
yParity: signature.yParity,
},
],
authorizationList: [authorization],
account: null, // defer to sequencer to fill
})

After authorize(), the contract field on Odyssey blockchain explorer would have this address:

0xef010035202a6e6317f3cc3a177eeee562d3bcda4a6fcc

As you can see, the delegation designator is set to this address, and the delegation contract is deployed with address: 35202a6e6317f3cc3a177eeee562d3bcda4a6fcc (see the implemenation)

Execute

After authorization, you can execute EOA through the delegation designator. And because the delegation contract already implement multiSend. So you can execute multiple calls within the same transaction.

// Fetch the next available nonce from the delegated EOA's contract.
const nonce = await readContract(client, {
abi: ExperimentDelegation.abi,
address: account.address,
functionName: 'nonce',
})

// Encode calls into format required by the contract.
const calls_encoded = concat(
calls.map((call) =>
encodePacked(
['uint8', 'address', 'uint256', 'uint256', 'bytes'],
[
0,
call.to,
call.value ?? 0n,
BigInt(size(call.data ?? '0x')),
call.data ?? '0x',
],
),
),
)

// Compute digest to sign for the execute function.
const digest = keccak256(
encodePacked(['uint256', 'bytes'], [nonce, calls_encoded]),
)

// Sign the digest with the authorized WebAuthn key.
const { signature, webauthn } = await sign({
hash: digest,
credentialId: account.key.id,
})

// Extract r and s values from signature.
const r = BigInt(slice(signature, 0, 32))
const s = BigInt(slice(signature, 32, 64))

// Execute calls.
return await writeContract(client, {
abi: ExperimentDelegation.abi,
address: account.address,
functionName: 'execute',
args: [calls_encoded, { r, s }, webauthn, 0],
account: null, // defer to sequencer to fill
})

This is what it looks like while sending the tokens to Alice and Bob in exp-0001 (see this)

Some Questions from my side

Are EIP4337 and EIP7702 compatible? And how do they do so, is there any examples?

Yes they are compatible.

Yes, see those Implementations below.

Does authorization decrease the probability of missing a secret key (the private key that the EOA owns)

Yes and no. If you can authorize your wallet with another way like passkey, then you can use your passkey to sign the message instead of taking out your private key and sign the tx. or you can just throw your private key away and make it vanish forever.

The reason for “NO” is because the account should be “self-custodied”, which means if you are not able to keep your secret key, you can turn to use other service instead :).

Can we change the authorization if the passkey is lost?

We can use the same EOA secret key to sign the authorization transaction again, which will overwrite the previous delegation.

Can an EOA delegate to multiple delegation designator?

No, since in the proposal it has: It will in the case of multiple tuples for the same authority, set the code using the address in the last valid occurrence. So, no, there will be only one active delegation designator at one time.

What if we want to delegate in multiple chains?

Delegating to a wallet proxy, you can set chain Id to 0 for validity on all EIP-7702 chains. And Wallet maintainers can also hard code a single EIP-7702 authorization message into their wallet so that cross-chain code compatibility is never a problem.

Closing

EIP-7702 marks a significant step toward bridging the gap between EOAs and smart contract accounts on Ethereum. By enabling an EOA “adopt” smart contract code via a delegation designator, this proposal makes current EOA overcome the limitations, namely, their reliance on single private key (no multi-sig recovery or other authentication method), lack of native programmability or gas fee sponsorship. With EIP-7702, users can now perform advanced operations such as batch transactions, custom authentications (like passkeys), and even multi-sig recoveries, all while keeping control of their private keys.

As we move closer to the Pectra hardfork in 2025, the evolution of account abstraction through EIP-7702 is one of the most promising developments in Ethereum’s roadmap. Although the code shows a great example to design in EIP-7702, I believe there will be more EIP-7702 related account and wallet coming out :).

Reference

Video

Implementations

Posts on Twitter

Linktree:

:) Donate to me: 0xF9B1810cFde1045d36eA198a07045a9F2bb47F32


EIP-7702 Introduction was originally published in Taipei Ethereum Meetup on Medium, where people are continuing the conversation by highlighting and responding to this story.

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

Taipei Ethereum Meetup
数据请求中
查看更多

推荐专栏

数据请求中
在 App 打开