Skip to content

Commit 2336a4f

Browse files
committed
fix: update ethers dependency to 6.14.3 and refactor authorization types to use native Authorization
1 parent 1adebfb commit 2336a4f

10 files changed

Lines changed: 78 additions & 122 deletions

File tree

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"chai-as-promised": "^7.1.1",
5757
"encoding-down": "^7.1.0",
5858
"ethereum-cryptography": "^2.0.0",
59-
"ethers": "6.13.1",
59+
"ethers": "6.14.3",
6060
"fast-text-encoding": "^1.0.6",
6161
"levelup": "^5.1.1",
6262
"msgpack-lite": "^0.1.26"

‎src/contracts/relay-adapt/V2/relay-adapt-7702.ts‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Result,
99
Log,
1010
toUtf8String,
11+
Authorization,
1112
} from 'ethers';
1213
import { ABIRelayAdapt, ABIRelayAdapt7702 } from '../../../abi/abi';
1314
import { TransactionReceiptLog } from '../../../models/formatted-types';
@@ -20,7 +21,6 @@ import { RelayAdapt } from '../../../abi/typechain/RelayAdapt';
2021
import { PayableOverrides } from '../../../abi/typechain/common';
2122
import { TransactionStructV2 } from '../../../models/transaction-types';
2223
import { MINIMUM_RELAY_ADAPT_CROSS_CONTRACT_CALLS_GAS_LIMIT_V2 } from '../constants';
23-
import { EIP7702Authorization } from '../../../models/relay-adapt-types';
2424
import { ByteUtils } from '../../../utils/bytes';
2525

2626
enum RelayAdaptEvent {
@@ -51,7 +51,7 @@ export class RelayAdapt7702Contract {
5151

5252
async populateShieldBaseToken(
5353
shieldRequest: ShieldRequestStruct,
54-
authorization?: EIP7702Authorization,
54+
authorization?: Authorization,
5555
signature?: string,
5656
random31Bytes?: string,
5757
ephemeralAddress?: string,
@@ -164,7 +164,7 @@ export class RelayAdapt7702Contract {
164164
random31Bytes: string,
165165
_useDummyProof: boolean,
166166
sendWithPublicWallet: boolean,
167-
authorization?: EIP7702Authorization,
167+
authorization?: Authorization,
168168
signature?: string,
169169
ephemeralAddress?: string,
170170
): Promise<ContractTransaction> {
@@ -270,7 +270,7 @@ export class RelayAdapt7702Contract {
270270
isGasEstimate: boolean,
271271
isBroadcasterTransaction: boolean,
272272
minGasLimit?: bigint,
273-
authorization?: EIP7702Authorization,
273+
authorization?: Authorization,
274274
signature?: string,
275275
ephemeralAddress?: string,
276276
): Promise<ContractTransaction> {
@@ -367,7 +367,7 @@ export class RelayAdapt7702Contract {
367367
private async populateRelayMulticall(
368368
calls: ContractTransaction[],
369369
overrides: PayableOverrides,
370-
authorization?: EIP7702Authorization,
370+
authorization?: Authorization,
371371
signature?: string,
372372
random31Bytes?: string,
373373
ephemeralAddress?: string,
@@ -403,7 +403,7 @@ export class RelayAdapt7702Contract {
403403
calls: ContractTransaction[],
404404
overrides: PayableOverrides,
405405
minimumGasLimit = BigInt(0),
406-
authorization?: EIP7702Authorization,
406+
authorization?: Authorization,
407407
signature?: string,
408408
ephemeralAddress?: string,
409409
): Promise<ContractTransaction> {

‎src/contracts/relay-adapt/relay-adapt-7702-helper.ts‎

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ContractTransaction, AbiCoder, keccak256, Wallet, encodeRlp, toBeHex, concat, Interface, HDNodeWallet } from 'ethers';
1+
import { ContractTransaction, AbiCoder, keccak256, Wallet, Interface, HDNodeWallet, Authorization } from 'ethers';
22
import { ByteUtils } from '../../utils/bytes';
33
import { ShieldNoteERC20 } from '../../note/erc20/shield-note-erc20';
44
import { AddressData, decodeAddress } from '../../key-derivation';
@@ -12,35 +12,25 @@ import { ShieldNoteNFT } from '../../note/nft/shield-note-nft';
1212
import { ERC721_NOTE_VALUE } from '../../note/note-util';
1313
import { RelayAdapt, ShieldRequestStruct } from '../../abi/typechain/RelayAdapt';
1414
import { TransactionStructV2, TransactionStructV3 } from '../../models/transaction-types';
15-
import { EIP7702Authorization } from '../../models/relay-adapt-types';
1615
import { ABIRelayAdapt7702 } from '../../abi/abi';
16+
import { signEIP7702Authorization as signEIP7702AuthorizationCore } from '../../transaction/eip7702';
1717

1818
class RelayAdapt7702Helper {
19+
/**
20+
* Signs an EIP-7702 Authorization using ethers native methods.
21+
* @param signer - The ephemeral key signer
22+
* @param contractAddress - The address to delegate to (RelayAdapt7702)
23+
* @param chainId - Chain ID
24+
* @param nonce - Nonce (typically 0 for ephemeral keys)
25+
* @returns Authorization tuple
26+
*/
1927
static async signEIP7702Authorization(
2028
signer: Wallet | HDNodeWallet,
2129
contractAddress: string,
2230
chainId: bigint,
2331
nonce: number,
24-
): Promise<EIP7702Authorization> {
25-
// 0x05 || rlp([chain_id, address, nonce])
26-
const rlpEncoded = encodeRlp([
27-
toBeHex(chainId),
28-
contractAddress,
29-
toBeHex(nonce),
30-
]);
31-
const payload = concat(['0x05', rlpEncoded]);
32-
const digest = keccak256(payload);
33-
34-
const signature = signer.signingKey.sign(digest);
35-
36-
return {
37-
chainId: chainId.toString(),
38-
address: contractAddress,
39-
nonce,
40-
yParity: signature.yParity,
41-
r: signature.r,
42-
s: signature.s,
43-
};
32+
): Promise<Authorization> {
33+
return signEIP7702AuthorizationCore(signer, contractAddress, chainId, nonce);
4434
}
4535

4636
static async signExecutionAuthorization(

‎src/contracts/relay-adapt/relay-adapt-versioned-smart-contracts.ts‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ContractTransaction, Provider, TransactionRequest } from 'ethers';
1+
import { ContractTransaction, Provider, TransactionRequest, Authorization } from 'ethers';
22
import { ContractStore } from '../contract-store';
33
import { Chain } from '../../models/engine-types';
44
import { TXIDVersion } from '../../models/poi-types';
@@ -7,7 +7,6 @@ import { TransactionReceiptLog, TransactionStructV2, TransactionStructV3 } from
77
import { RelayAdaptV2Contract } from './V2/relay-adapt-v2';
88
import { RelayAdapt7702Contract } from './V2/relay-adapt-7702';
99
import { RelayAdaptV3Contract } from './V3/relay-adapt-v3';
10-
import { EIP7702Authorization } from '../../models/relay-adapt-types';
1110

1211
export class RelayAdaptVersionedSmartContracts {
1312
static getRelayAdaptContract(txidVersion: TXIDVersion, chain: Chain, isRelayAdapt7702 = false) {
@@ -30,7 +29,7 @@ export class RelayAdaptVersionedSmartContracts {
3029
chain: Chain,
3130
shieldRequest: ShieldRequestStruct,
3231
isRelayAdapt7702 = false,
33-
authorization?: EIP7702Authorization,
32+
authorization?: Authorization,
3433
signature?: string,
3534
random31Bytes?: string,
3635
ephemeralAddress?: string,
@@ -59,7 +58,7 @@ export class RelayAdaptVersionedSmartContracts {
5958
useDummyProof: boolean,
6059
sendWithPublicWallet: boolean,
6160
isRelayAdapt7702 = false,
62-
authorization?: EIP7702Authorization,
61+
authorization?: Authorization,
6362
signature?: string,
6463
ephemeralAddress?: string,
6564
): Promise<ContractTransaction> {
@@ -110,7 +109,7 @@ export class RelayAdaptVersionedSmartContracts {
110109
isBroadcasterTransaction: boolean,
111110
minGasLimit?: bigint,
112111
isRelayAdapt7702 = false,
113-
authorization?: EIP7702Authorization,
112+
authorization?: Authorization,
114113
signature?: string,
115114
ephemeralAddress?: string,
116115
): Promise<ContractTransaction> {

‎src/models/relay-adapt-types.ts‎

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
1+
import { Authorization } from 'ethers';
12
import { TransactionStructV2 } from './transaction-types';
23
import { RelayAdapt } from '../abi/typechain/RelayAdapt';
34

4-
export interface EIP7702Authorization {
5-
chainId: string;
6-
address: string;
7-
nonce: number;
8-
yParity: 0 | 1;
9-
r: string;
10-
s: string;
11-
}
12-
135
export interface RelayAdapt7702Request {
146
transactions: TransactionStructV2[];
157
actionData: RelayAdapt.ActionDataStruct;
16-
authorization: EIP7702Authorization;
8+
authorization: Authorization;
179
executionSignature: string;
1810
ephemeralAddress: string;
1911
}

‎src/transaction/__tests__/eip7702.test.ts‎

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,24 @@ function toRlpInteger(value: number): string | Uint8Array {
1010
}
1111

1212
describe('EIP-7702 Signing', () => {
13-
it('should sign and recover correctly', () => {
13+
it('should sign and recover correctly', async () => {
1414
const signer = Wallet.createRandom();
1515
const contractAddress = '0x1234567890123456789012345678901234567890';
16-
const chainId = 1;
16+
const chainId = 1n;
1717
const nonce = 0;
1818

19-
const auth = signEIP7702Authorization(signer, contractAddress, chainId, nonce);
19+
const auth = await signEIP7702Authorization(signer, contractAddress, chainId, nonce);
2020

2121
// Reconstruct hash
2222
const rlpEncoded = encodeRlp([
23-
toRlpInteger(chainId),
23+
toRlpInteger(Number(chainId)),
2424
contractAddress,
2525
toRlpInteger(nonce)
2626
]);
2727
const payload = new Uint8Array([0x05, ...getBytes(rlpEncoded)]);
2828
const hash = keccak256(payload);
2929

30-
const recovered = recoverAddress(hash, {
31-
r: auth.r,
32-
s: auth.s,
33-
yParity: auth.yParity,
34-
});
30+
const recovered = recoverAddress(hash, auth.signature);
3531

3632
expect(recovered).to.equal(signer.address);
3733
});

‎src/transaction/eip7702.ts‎

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,27 @@
1-
import { HDNodeWallet, Wallet, keccak256, encodeRlp, getBytes, toBeHex } from 'ethers';
2-
import { EIP7702Authorization } from '../models/relay-adapt-types';
3-
4-
function toRlpInteger(value: number): string | Uint8Array {
5-
if (value === 0) {
6-
return new Uint8Array(0);
7-
}
8-
return toBeHex(value);
9-
}
1+
import { HDNodeWallet, Wallet, Authorization } from 'ethers';
102

113
/**
12-
* Signs an EIP-7702 Authorization Tuple.
13-
* Payload: 0x05 || rlp([chain_id, address, nonce])
4+
* Signs an EIP-7702 Authorization Tuple using ethers native methods.
145
* @param signer - The ephemeral key signer
156
* @param contractAddress - The address to delegate to (RelayAdapt7702)
16-
* @param chainId - Chain ID
17-
* @param nonce - Nonce (default 0)
18-
* @returns EIP7702Authorization
7+
* @param chainId - Chain ID (optional - will be auto-populated if not provided)
8+
* @param nonce - Nonce (optional - will be auto-populated if not provided)
9+
* @returns Authorization
1910
*/
20-
export const signEIP7702Authorization = (
11+
export const signEIP7702Authorization = async (
2112
signer: HDNodeWallet | Wallet,
2213
contractAddress: string,
23-
chainId: number,
24-
nonce: number = 0
25-
): EIP7702Authorization => {
26-
// RLP encode the tuple [chain_id, address, nonce]
27-
// We use toRlpInteger to ensure numbers are formatted correctly for RLP (0 -> empty bytes, others -> hex)
28-
const rlpEncoded = encodeRlp([
29-
toRlpInteger(chainId),
30-
contractAddress,
31-
toRlpInteger(nonce)
32-
]);
33-
34-
// Prepend 0x05 (EIP-7702 transaction type / magic byte)
35-
const payload = new Uint8Array([0x05, ...getBytes(rlpEncoded)]);
36-
const hash = keccak256(payload);
37-
38-
// Sign the hash directly (not EIP-191)
39-
const sig = signer.signingKey.sign(hash);
40-
41-
return {
42-
chainId: chainId.toString(),
14+
chainId?: bigint,
15+
nonce?: number,
16+
): Promise<Authorization> => {
17+
// Use ethers 6.14.3+ native 7702 authorization signing
18+
const authRequest = await signer.populateAuthorization({
4319
address: contractAddress,
44-
nonce,
45-
yParity: sig.yParity,
46-
r: sig.r,
47-
s: sig.s,
48-
};
20+
...(chainId !== undefined && { chainId }),
21+
...(nonce !== undefined && { nonce: BigInt(nonce) }),
22+
});
23+
24+
return signer.authorize(authRequest);
4925
};
26+
27+

‎src/validation/relay-adapt-7702-validator.ts‎

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,32 @@
1-
import { verifyMessage, getBytes, keccak256, AbiCoder, encodeRlp, toBeHex, recoverAddress } from 'ethers';
1+
import { verifyMessage, getBytes, keccak256, AbiCoder, encodeRlp, toBeHex, recoverAddress, Authorization } from 'ethers';
22
import { TransactionStructV2 } from '../models/transaction-types';
33
import { RelayAdapt7702 } from '../abi/typechain/RelayAdapt7702';
4-
import { EIP7702Authorization } from '../models/relay-adapt-types';
54
import { ACTION_DATA_STRUCT_ABI, TRANSACTION_STRUCT_ABI } from '../transaction/relay-adapt-7702-signature';
65

76
export class RelayAdapt7702Validator {
87
static validateAuthorization(
9-
authorization: EIP7702Authorization,
8+
authorization: Authorization,
109
expectedContractAddress: string,
1110
expectedChainId: number
1211
): string {
1312
if (authorization.address.toLowerCase() !== expectedContractAddress.toLowerCase()) {
1413
throw new Error('Authorization contract address mismatch');
1514
}
16-
if (BigInt(authorization.chainId) !== BigInt(expectedChainId)) {
15+
if (authorization.chainId !== BigInt(expectedChainId)) {
1716
throw new Error('Authorization chain ID mismatch');
1817
}
1918

2019
// Reconstruct payload
2120
const rlpEncoded = encodeRlp([
22-
authorization.chainId === '0' ? new Uint8Array(0) : toBeHex(BigInt(authorization.chainId)),
21+
authorization.chainId === 0n ? new Uint8Array(0) : toBeHex(authorization.chainId),
2322
authorization.address,
24-
authorization.nonce === 0 ? new Uint8Array(0) : toBeHex(authorization.nonce)
23+
authorization.nonce === 0n ? new Uint8Array(0) : toBeHex(authorization.nonce)
2524
]);
2625
const payload = new Uint8Array([0x05, ...getBytes(rlpEncoded)]);
2726
const hash = keccak256(payload);
2827

2928
// Recover address
30-
return recoverAddress(hash, {
31-
r: authorization.r,
32-
s: authorization.s,
33-
yParity: authorization.yParity
34-
});
29+
return recoverAddress(hash, authorization.signature);
3530
}
3631

3732
static validateExecution(

‎src/wallet/railgun-wallet.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ import { Mnemonic } from '../key-derivation/bip39';
1010
import { PublicInputsRailgun } from '../models';
1111
import { signEDDSA } from '../utils/keys-utils';
1212
import { Prover } from '../prover/prover';
13-
import { HDNodeWallet } from 'ethers';
13+
import { HDNodeWallet, Authorization } from 'ethers';
1414
import { RelayAdapt7702Helper } from '../contracts/relay-adapt/relay-adapt-7702-helper';
15-
import { EIP7702Authorization } from '../models/relay-adapt-types';
1615
import { TransactionStructV2, TransactionStructV3 } from '../models/transaction-types';
1716
import { RelayAdapt } from '../abi/typechain/RelayAdapt';
1817

@@ -88,15 +87,15 @@ class RailgunWallet extends AbstractWallet {
8887
* @param {bigint} chainId - Chain ID
8988
* @param {(TransactionStructV2 | TransactionStructV3)[]} transactions - Railgun transactions
9089
* @param {RelayAdapt.ActionDataStruct} actionData - Action Data
91-
* @returns {Promise<{ authorization: EIP7702Authorization; signature: string }>}
90+
* @returns {Promise<{ authorization: Authorization; signature: string }>}
9291
*/
9392
async sign7702Request(
9493
encryptionKey: string,
9594
contractAddress: string,
9695
chainId: bigint,
9796
transactions: (TransactionStructV2 | TransactionStructV3)[],
9897
actionData: RelayAdapt.ActionDataStruct,
99-
): Promise<{ authorization: EIP7702Authorization; signature: string }> {
98+
): Promise<{ authorization: Authorization; signature: string }> {
10099
const index = await this.getEphemeralKeyIndex();
101100
const ephemeralWallet = await this.getEphemeralWallet(encryptionKey, index);
102101

0 commit comments

Comments
 (0)