Support EIP 7702
Describe the Feature
Add support for tx type 0x04 and authorization_list in transaction types
https://eips.ethereum.org/EIPS/eip-7702
Code Example
No response
Oh! Is it finally deployed on a testnet? I’ve been waiting for that, as that is a good indication the spec of likely done changing. Plus it enables trying it out to test it. :)
Initially this will be available on signer._authorization() and once official, will be signer.authorization().
@ricmoo it's deployed on Pectra Devnet, Odyssey and a few others. Looking forward to this
FYI https://blog.ethereum.org/2025/02/14/pectra-testnet-announcement
It will go live on Holesky at epoch 115968 (Feb. 24, 21:55 UTC) and then on Sepolia at epoch 222464 (Mar. 5, 7:29 UTC).
Thanks! Yes, I have a local implementation (in BaseWallet and Transaction) ready to release on the 24th once I can test it out. :)
@ricmoo let me know if this requires any contribution, as I've already tested this out on mekong
any way I can initiate the new transaction type already with ethers?
Almost. I have a PR I'll be pushing up later. I'm just waiting for it to go live on Sepolia so I can make sure my tests work. :)
Does anyone know of a JSON-RPC endpoint for Sepolia that this works on? It should have definitely activated by now. I've tried Infura, but I get the error:
// Error:
{
code: -32602,
message: 'transaction could not be decoded: unsupported transaction type'
}
// Payload:
{
method: 'eth_sendRawTransaction',
params: [
'0x04f8c383aa36a70e80808252089401234567890123456789012345678901234567898080c0f85cf85a809401234567890123456789012345678901234567898080a0f063d1abb04fa98cfa4dfd92d509efcd398ab63b6d9c2944a1827d631f310b50a065c40ea1fa228700da4a53bdd1fc94d2b67ee23121466af3cf56f37ecc70c37a01a074c7c3b64a353009ab12c1c81406e381c83a7452be4513c23356816922e09c38a052d74f0486ac363f2c83b4dca8f099f09d3400a7469efb553195efa6fe94aca8'
],
id: 8,
jsonrpc: '2.0'
}
But that could just mean my encoding is incorrect or there is some other issue and the error is half red-herring. If someone has a know good JSON-RPC endpoint, it'd be appreciated. :)
I've added a branch with initial support. I'm going to test it a bit more and then publish it as an alpha release on npm.
To use it:
// You may optionally specify a nonce and chainId; if omitted the current provider is used
// to get the chain ID and nonce. A chainID of 0 must be explicitly set.
const auth = await authSigner.authorize({ address })
// The sending wallet can be the same signer or any other signer
const tx = await sendSigner.sendTransaction({
type: 4,
to: someAddress,
authorizationList: [ auth ]
});
console.log(tx);
const receipt = await tx.wait();
console.log(receipt);
Is there a way to delegate and call in the same tx, something like this?
import { parseEther } from 'viem'
import { walletClient } from './config'
import { abi, contractAddress } from './contract'
// 1. Authorize injection of the Contract's bytecode into our Account.
const authorization = await walletClient.signAuthorization({
contractAddress,
})
// 2. Invoke the Contract's `execute` function to perform batch calls.
const hash = await walletClient.sendTransaction({
authorizationList: [authorization],
data: encodeFunctionData({
abi,
functionName: 'execute',
args: [
[
{
data: '0x',
to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
value: parseEther('0.001'),
},
{
data: '0x',
to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
value: parseEther('0.002'),
},
],
]
}),
to: walletClient.account.address,
})
Refs: https://viem.sh/experimental/eip7702/sending-transactions
Yes, the above code (converted to ethers syntax) would work just fine on the new branch.
But if that is a common pattern, I think it also makes sense to add authorizationList to the ContractOverrides interface, so a more object-oriented usage can be used like contract.execute([ arg0, arg1 ], { authorizationList: [ authorization ] }). I can add that to the Contract class tomorrow. :)
Having that in the contract class is a must IMO
It is worth noting in the current Ethers implementation that the below usage will break when authSigner is the actor signing the authorization and sending the transaction as the authorization tuple’s nonce must be incremented by an extra one (ie. the next nonce after the 7702 tx).
const auth = await authSigner.authorize({ address })
const tx = await authSigner.sendTransaction({
type: 4,
to: someAddress,
authorizationList: [ auth ]
});
console.log(tx);
const receipt = await tx.wait();
console.log(receipt);
Good to know! Thanks. Does Viem have special logic to detect this and populate it? Or does it rely on the authSigner to manually supply the bumped nonce?
There's no special logic unfortunately – we have a sponsor flag on signAuthorization (a la authorize) which will indicate that the authorization will be executed by another signer. Our 7702 is still experimental, so still thinking about the best approach for this though.
It's a shame that this behavior isn't handled on the execution layer though.
Yeah. Ditto, still experimenting with ideas.
I wonder if it would make sense to have some sort of DeferredNonce object or callback that TransactionRequest would allow in authorizationList items, which would get called with the populated transaction, giving it a chance to update before the actually signing based on the populated transaction…
Something like (tx: TransactionRequest) => Promise<Authorization> and a helper function that takes in a Signer and returns a function that checks if the tx.from matches, account for it, otherwise use the default action.
Yeah, I thought about getting smart like that. I wonder if the implementation complexity is worth it though if this feature will only be used by Wallets (who I would assume know what they are doing :P) – whereby, the current Ethers implementation would be fine, and they can just increment the nonce themself if they want the EOA to self-upgrade... and maybe self-upgrading wouldn't be a much common case anyway?
will this ship for both v5 and v6?
@douglance
This is already available in v6 (on the experimental eip-7703 branch and the npm beta-eip-7702 tagged version).
There are no plans to make any feature changes to v5. There was recently a v5 release to address two security concerns in its dependencies (keep in mind the elliptic issue did not actually affect Ethers, but did trigger audit warnings), prior to that the v5 code has not been touched in 3 years.
In general, any new projects should be using v6, which has new features added as the Ethereum networks add them (recent examples include BLoB and EIP-7702 support). The v5 code is primarily maintained for those that require pre-2017 era browser support. If you need any help migrating to v6 though, please reach out. :)
Is there any date for including this in a release?
This is available on npm via the above tag and I believe the release on mainnet is scheduled for May 8th.
I haven’t had any negative feedback yet for the current API and know it’s been in use, so I plan to get it out in the latest tag in the next couple of days. :)
But please try it out and provide any additional feedback. :)
Hey @ricmoo, I see 6.13.7 was released a few days ago.
Currently, I'm trying to enable 6.13.6-beta.1 to use authorizations in OZ Contracts. However, on every installation it jumps straight to 6.13.7 so I gotta fix the version package. All this is cool, but fixing the version creates a dependency conflict:
npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: @nomicfoundation/[email protected]
npm error Found: [email protected]
npm error node_modules/ethers
npm error dev ethers@"6.13.6-beta.1" from the root project
npm error
npm error Could not resolve dependency:
npm error peer ethers@"^6.1.0" from @nomicfoundation/[email protected]
npm error node_modules/@nomicfoundation/hardhat-chai-matchers
npm error dev @nomicfoundation/hardhat-chai-matchers@"^2.0.6" from the root project
npm error
npm error Conflicting peer dependency: [email protected]
npm error node_modules/ethers
npm error peer ethers@"^6.1.0" from @nomicfoundation/[email protected]
npm error node_modules/@nomicfoundation/hardhat-chai-matchers
npm error dev @nomicfoundation/hardhat-chai-matchers@"^2.0.6" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
As seen in this PR, we'll rely on --legacy-peer-deps for now. But, it'd be cool if we can have a 6.3.8 with 7702 support soon, otherwise a 6.3.8-beta.1 just to get rid of the dependency issue would be fine
The v6.13.6 version was meant to be a -beta.1 release and is identical to the release at the tag beta-eip-7702. The v6.13.6 was never made latest intentionally. The Changeling has been updated.
That said, v6.14.0 is now latest on npm, which has EIP-7702 support, so you no longer need to use the beta-eip-7702 tag. The standard release has it.
Let me know how your EIP-7702 adventures go. :)
All working good now, thanks @ricmoo 😄
Why can't I send a type 4 transaction using a different "from" address?
const tx = await c.sendTransaction({
type: 4,
from: a.address,
authorizationList: [auth],
paymaster: PAYMASTER,
paymasterInput: "0x",
to: B,
value: parseEther("100"),
gasLimit: 120_000
});
console.log("hash =", tx.hash);
const rcpt = await tx.wait();
console.log("gasUsed =", rcpt.gasUsed.toString());
And get error:
TypeError: transaction from mismatch (argument="tx.from", value="0x8943545177806ED17B9F23F0a21ee5948eCaa776", code=INVALID_ARGUMENT, version=6.14.0)
at makeError (file:///Users/smallyu/work/github/eip-7702-demo/node_modules/ethers/lib.esm/utils/errors.js:117:21)
at assert (file:///Users/smallyu/work/github/eip-7702-demo/node_modules/ethers/lib.esm/utils/errors.js:143:15)
at assertArgument (file:///Users/smallyu/work/github/eip-7702-demo/node_modules/ethers/lib.esm/utils/errors.js:154:5)
at file:///Users/smallyu/work/github/eip-7702-demo/node_modules/ethers/lib.esm/providers/abstract-signer.js:29:13
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async Promise.all (index 1)
at async resolveProperties (file:///Users/smallyu/work/github/eip-7702-demo/node_modules/ethers/lib.esm/utils/properties.js:33:21)
at async populate (file:///Users/smallyu/work/github/eip-7702-demo/node_modules/ethers/lib.esm/providers/abstract-signer.js:36:12)
at async Wallet.populateTransaction (file:///Users/smallyu/work/github/eip-7702-demo/node_modules/ethers/lib.esm/providers/abstract-signer.js:64:21)
at async Wallet.sendTransaction (file:///Users/smallyu/work/github/eip-7702-demo/node_modules/ethers/lib.esm/providers/abstract-signer.js:196:21) {
code: 'INVALID_ARGUMENT',
argument: 'tx.from',
value: '0x8943545177806ED17B9F23F0a21ee5948eCaa776',
shortMessage: 'transaction from mismatch'
}
ethersjs version:
"ethers": "^6.14.0"
Hi @ricmoo , I encounter a problem when trying to estimate gas for a type 4 transactions, it looks like an edge case :
const provider = new JsonRpcProvider(new FetchRequest('https://ethereum-holesky-rpc.publicnode.com'));
const result = await provider.estimateGas({
authorizationList: [{
address: '0x18655A180a4F954BE49E2A5f84b19a7D2101020f',
chainId: 17000,
nonce: 0,
signature: '0xd67e90caf8e196d5bd0b794077d24b37541dbd43b204bb9d1f744552a6168fb00e3e29cd0ff4e06284810eefeee2cb3a162ab953c7eca7167c8bd555bedc2c421c'
}],
type: 4,
nonce: 0,
to: '0x3e85262a8b2dbc934f04716da609cd0abc7724a0',
from: '0xe7a0aca36fb631d170f2e025c16756494b80a4a6',
data: '....'
})
The call being done to the RPC endpoint is the following:
{
"id": 6,
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [
{
"authorizationList": [
{
"address": "0x18655A180a4F954BE49E2A5f84b19a7D2101020f",
"chainId": "0x4268",
"nonce": "0x0",
"r": "0xd67e90caf8e196d5bd0b794077d24b37541dbd43b204bb9d1f744552a6168fb0",
"s": "0x0e3e29cd0ff4e06284810eefeee2cb3a162ab953c7eca7167c8bd555bedc2c42",
"yParity": "0x1"
}
],
"data": "...",
"from": "0xe7a0aca36fb631d170f2e025c16756494b80a4a6",
"nonce": "0x0",
"to": "0x3e85262a8b2dbc934f04716da609cd0abc7724a0",
"type": "0x4"
}
]
}
This will fail because the RPC endpoint returns:
{
"jsonrpc": "2.0",
"id": 6,
"error": {
"code": -32602,
"message": "invalid argument 0: json: cannot unmarshal hex number with leading zero digits into Go struct field TransactionArgs.authorizationList.s of type *uint256.Int"
}
}
I believe the s field should be 0xe3e29cd0ff4e06284810eefeee2cb3a162ab953c7eca7167c8bd555bedc2c42 instead of 0x0e3e29cd0ff4e06284810eefeee2cb3a162ab953c7eca7167c8bd555bedc2c42
I have monkey patched the library to make it work:
https://github.com/ethers-io/ethers.js/blob/d2c9ca0e0fd15e7884bcaab7d5152d68662e3e43/src.ts/providers/provider-jsonrpc.ts#L861-L873
if (tx.authorizationList) {
result["authorizationList"] = tx.authorizationList.map((_a) => {
const a = authorizationify(_a);
return {
address: a.address,
nonce: toQuantity(a.nonce),
chainId: toQuantity(a.chainId),
yParity: toQuantity(a.signature.yParity),
r: toQuantity(a.signature.r),
s: toQuantity(a.signature.s),
}
});
}
Should the library be updated, or am I doing something wrong?
Hi everyone. Can someone tell me what I’m doing wrong?
ethersProvider = new ethers.BrowserProvider(provider, 'any');
signer = await ethersProvider.getSigner();
userAddress = await signer.getAddress();
const auth = await signer.authorize({ address: userAddress });
I’m getting an error: Error: authorization not implemented for this signer (operation="authorize", code=UNSUPPORTED_OPERATION, version=6.14.0)
@adriendfns What network and node are you using? The leading-zero problem should only matter for values that are a quantity, while the r and s should be treated as data values.
I’ll verify with the Geth team and get back to you shortly.
@fredokruger A JsonRpcSigner currently has bot method to initiate an EIP-7702 authorize request (afaik; please correct me if I’m wrong).
The Ethers signers work because they have access to the private key directly, but using Geth/MetaMask I don’t believe there is currently an RPC method they expose for it.