Skip to content

Commit ae8bee2

Browse files
committed
feat(sdk): simplified EIP-7702 support
1 parent 78a163c commit ae8bee2

17 files changed

+185
-372
lines changed

.changeset/clever-beds-knock.md

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@
55
Feature: Adds beta support for EIP-7702 authorization lists
66

77
```ts
8-
import { prepareTransaction, sendTransaction } from "thirdweb";
8+
import { prepareTransaction, sendTransaction, signAuthorization } from "thirdweb";
9+
10+
const authorization = await signAuthorization({
11+
request: {
12+
address: "0x...",
13+
chainId: 911867,
14+
nonce: 100n,
15+
},
16+
account: myAccount,
17+
});
918

1019
const transaction = prepareTransaction({
1120
chain: ANVIL_CHAIN,
1221
client: TEST_CLIENT,
1322
value: 100n,
1423
to: TEST_WALLET_B,
15-
authorizations: [
16-
{
17-
address: "0x...",
18-
chainId: 1,
19-
nonce: 420n,
20-
},
21-
],
24+
authorizationList: authorization,
2225
});
2326

2427
const res = await sendTransaction({
@@ -27,28 +30,3 @@ const res = await sendTransaction({
2730
});
2831
```
2932

30-
You can access the underlying authorization signing functions like so:
31-
32-
```ts
33-
import { signAuthorization, signedAuthorizations } from "thirdweb";
34-
35-
const signedAuthorization = await signAuthorization({
36-
authorization: {
37-
address: "0x...",
38-
chainId: 1,
39-
nonce: 420n,
40-
},
41-
account: myAccount,
42-
});
43-
44-
const signedAuthorizations = await signedAuthorizations({
45-
authorizations: [
46-
{
47-
address: "0x...",
48-
chainId: 1,
49-
nonce: 420n,
50-
},
51-
],
52-
account: myAccount,
53-
});
54-
```

packages/thirdweb/src/exports/thirdweb.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,12 @@ export {
302302
type VerifyTypedDataParams,
303303
verifyTypedData,
304304
} from "../auth/verify-typed-data.js";
305+
306+
/**
307+
* EIP-7702
308+
*/
309+
export type {
310+
AuthorizationRequest,
311+
SignedAuthorization,
312+
} from "../transaction/actions/eip7702/authorization.js";
313+
export { signAuthorization } from "../transaction/actions/eip7702/authorization.js";

packages/thirdweb/src/exports/transaction.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,12 @@ export type { GaslessOptions } from "../transaction/actions/gasless/types.js";
7878
export type { EngineOptions } from "../transaction/actions/gasless/providers/engine.js";
7979
export type { OpenZeppelinOptions } from "../transaction/actions/gasless/providers/openzeppelin.js";
8080
export type { BiconomyOptions } from "../transaction/actions/gasless/providers/biconomy.js";
81+
82+
/**
83+
* EIP-7702
84+
*/
85+
export type {
86+
AuthorizationRequest,
87+
SignedAuthorization,
88+
} from "../transaction/actions/eip7702/authorization.js";
89+
export { signAuthorization } from "../transaction/actions/eip7702/authorization.js";
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { describe, expect, it } from "vitest";
2+
import { TEST_WALLET_B } from "~test/addresses.js";
3+
import { TEST_ACCOUNT_A } from "~test/test-wallets.js";
4+
import { signAuthorization } from "./authorization.js";
5+
6+
describe("signAuthorization", () => {
7+
it("should sign an authorization", async () => {
8+
const authorization = await signAuthorization({
9+
account: TEST_ACCOUNT_A,
10+
request: {
11+
address: TEST_WALLET_B,
12+
chainId: 911867,
13+
nonce: 0n,
14+
},
15+
});
16+
expect(authorization).toMatchInlineSnapshot(`
17+
{
18+
"address": "0x0000000000000000000000000000000000000002",
19+
"chainId": 911867,
20+
"nonce": 0n,
21+
"r": 3720526934953059641417422884731844424204826752871127418111522219225437830766n,
22+
"s": 23451045058292828843243765241045958975073226494910356096978666517928790374894n,
23+
"yParity": 1,
24+
}
25+
`);
26+
});
27+
});
Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,58 @@
11
import type * as ox__Authorization from "ox/Authorization";
22
import type { Address } from "../../../utils/address.js";
3+
import type { Account } from "../../../wallets/interfaces/wallet.js";
34

45
/**
5-
* An EIP-7702 authorization input object.
6+
* An EIP-7702 authorization object fully prepared and ready for signing.
67
*
78
* @beta
89
* @transaction
910
*/
10-
export type Authorization = {
11+
export type AuthorizationRequest = {
1112
address: Address;
12-
chainId?: number;
13-
nonce?: bigint;
13+
chainId: number;
14+
nonce: bigint;
1415
};
1516

1617
/**
17-
* An EIP-7702 authorization object fully prepared and ready for signing.
18+
* Represents a signed EIP-7702 authorization object.
1819
*
1920
* @beta
2021
* @transaction
2122
*/
22-
export type PreparedAuthorization = {
23-
address: Address;
24-
chainId: number;
25-
nonce: bigint;
26-
};
23+
export type SignedAuthorization = ox__Authorization.ListSigned[number];
2724

2825
/**
29-
* Represents a signed EIP-7702 authorization object.
26+
* Sign the given EIP-7702 authorization object.
27+
* @param options - The options for `signAuthorization`
28+
* Refer to the type [`SignAuthorizationOptions`](https://portal.thirdweb.com/references/typescript/v5/SignAuthorizationOptions)
29+
* @returns The signed authorization object
30+
*
31+
* ```ts
32+
* import { signAuthorization } from "thirdweb";
33+
*
34+
* const authorization = await signAuthorization({
35+
* request: {
36+
* address: "0x...",
37+
* chainId: 911867,
38+
* nonce: 100n,
39+
* },
40+
* account: myAccount,
41+
* });
42+
* ```
3043
*
3144
* @beta
3245
* @transaction
3346
*/
34-
export type SignedAuthorization = ox__Authorization.ListSigned[number];
47+
export async function signAuthorization(options: {
48+
account: Account;
49+
request: AuthorizationRequest;
50+
}): Promise<SignedAuthorization> {
51+
const { account, request } = options;
52+
if (typeof account.signAuthorization === "undefined") {
53+
throw new Error(
54+
"This account type does not yet support signing EIP-7702 authorizations",
55+
);
56+
}
57+
return account.signAuthorization(request);
58+
}

packages/thirdweb/src/transaction/actions/estimate-gas.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1+
import * as ox__Hex from "ox/Hex";
12
import { formatTransactionRequest } from "viem";
23
import { roundUpGas } from "../../gas/op-gas-fee-reducer.js";
34
import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value.js";
45
import type { Prettify } from "../../utils/type-utils.js";
56
import type { Account } from "../../wallets/interfaces/wallet.js";
67
import { extractError } from "../extract-error.js";
78
import type { PreparedTransaction } from "../prepare-transaction.js";
8-
import { resolveAndSignAuthorizations } from "./to-serializable-transaction.js";
9-
import * as ox__Hex from "ox/Hex";
109

1110
export type EstimateGasOptions = Prettify<
1211
{
@@ -18,21 +17,21 @@ export type EstimateGasOptions = Prettify<
1817
transaction: PreparedTransaction<any>;
1918
} & (
2019
| {
21-
/**
22-
* The account the transaction would be sent from.
23-
*
24-
* @deprecated Use `from` instead
25-
*/
26-
account: Account;
27-
from?: never;
28-
}
20+
/**
21+
* The account the transaction would be sent from.
22+
*
23+
* @deprecated Use `from` instead
24+
*/
25+
account: Account;
26+
from?: never;
27+
}
2928
| {
30-
account?: never;
31-
/**
32-
* The address the transaction would be sent from.
33-
*/
34-
from?: string | Account;
35-
}
29+
account?: never;
30+
/**
31+
* The address the transaction would be sent from.
32+
*/
33+
from?: string | Account;
34+
}
3635
)
3736
>;
3837

@@ -66,8 +65,8 @@ export async function estimateGas(
6665
// 3. the passed in wallet's account address
6766
const fromAddress =
6867
typeof options.from === "string"
69-
? options.from ?? undefined
70-
: options.from?.address ?? options.account?.address;
68+
? (options.from ?? undefined)
69+
: (options.from?.address ?? options.account?.address);
7170
const txWithFrom = { ...options.transaction, from: fromAddress };
7271
if (cache.has(txWithFrom)) {
7372
// biome-ignore lint/style/noNonNullAssertion: the `has` above ensures that this will always be set
@@ -104,7 +103,7 @@ export async function estimateGas(
104103
encode(options.transaction),
105104
resolvePromisedValue(options.transaction.to),
106105
resolvePromisedValue(options.transaction.value),
107-
resolveAndSignAuthorizations(options),
106+
resolvePromisedValue(options.transaction.authorizationList),
108107
]);
109108

110109
// load up the rpc client and the estimateGas function if we need it
@@ -132,6 +131,7 @@ export async function estimateGas(
132131
})),
133132
}),
134133
);
134+
135135
if (options.transaction.chain.experimental?.increaseZeroByteCount) {
136136
gas = roundUpGas(gas);
137137
}

packages/thirdweb/src/transaction/actions/send-transaction.test.ts

Lines changed: 1 addition & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,9 @@ import { describe, expect, it, vi } from "vitest";
22
import { TEST_WALLET_B } from "../../../test/src/addresses.js";
33
import { ANVIL_CHAIN } from "../../../test/src/chains.js";
44
import { TEST_CLIENT } from "../../../test/src/test-clients.js";
5-
import {
6-
TEST_ACCOUNT_A,
7-
TEST_ACCOUNT_B,
8-
} from "../../../test/src/test-wallets.js";
9-
import { defineChain } from "../../chains/utils.js";
10-
import { getContract } from "../../contract/contract.js";
11-
import { claimTo } from "../../extensions/erc20/drops/write/claimTo.js";
12-
import { getWalletBalance } from "../../wallets/utils/getWalletBalance.js";
13-
import { prepareContractCall } from "../prepare-contract-call.js";
5+
import { TEST_ACCOUNT_A } from "../../../test/src/test-wallets.js";
146
import { prepareTransaction } from "../prepare-transaction.js";
157
import * as TransactionStore from "../transaction-store.js";
16-
import { encode } from "./encode.js";
17-
import { sendAndConfirmTransaction } from "./send-and-confirm-transaction.js";
188
import { sendTransaction } from "./send-transaction.js";
199

2010
const addTransactionToStore = vi.spyOn(
@@ -38,103 +28,6 @@ describe("sendTransaction", () => {
3828
expect(res.transactionHash.length).toBe(66);
3929
});
4030

41-
it.only("should send an eip7702 transaction", async () => {
42-
const account = TEST_ACCOUNT_A;
43-
const executor = TEST_ACCOUNT_B;
44-
45-
console.log("ACCOUNT", account.address);
46-
console.log("DELEGATE", executor.address);
47-
48-
const chain = defineChain(911867);
49-
const tokenContract = getContract({
50-
address: "0xAA462a5BE0fc5214507FDB4fB2474a7d5c69065b",
51-
chain,
52-
client: TEST_CLIENT,
53-
});
54-
55-
const claimTx = claimTo({
56-
contract: tokenContract,
57-
quantityInWei: 1000n,
58-
to: account.address,
59-
});
60-
61-
// const transferTx = transfer({
62-
// contract: tokenContract,
63-
// amountWei: 1000n,
64-
// to: "0x2247d5d238d0f9d37184d8332aE0289d1aD9991b",
65-
// });
66-
67-
console.log(
68-
"BALANCE BEFORE",
69-
await getWalletBalance({
70-
address: account.address,
71-
chain,
72-
client: TEST_CLIENT,
73-
tokenAddress: tokenContract.address,
74-
}),
75-
);
76-
77-
// const nonce = await (async () => {
78-
// const rpcRequest = getRpcClient({
79-
// chain,
80-
// client: TEST_CLIENT,
81-
// });
82-
// const { eth_getTransactionCount } = await import(
83-
// "../../rpc/actions/eth_getTransactionCount.js"
84-
// );
85-
// return await eth_getTransactionCount(rpcRequest, {
86-
// address: account.address,
87-
// blockTag: "pending",
88-
// });
89-
// })();
90-
91-
// const signedAuthorization = await account.signAuthorization!({
92-
// address: "0x654F42b74885EE6803F403f077bc0409f1066c58",
93-
// chainId: chain.id,
94-
// nonce: BigInt(nonce),
95-
// });
96-
97-
// console.log("SIGNED AUTHORIZATION", signedAuthorization);
98-
99-
const batchSend = prepareContractCall({
100-
contract: getContract({
101-
address: account.address,
102-
chain,
103-
client: TEST_CLIENT,
104-
}),
105-
method:
106-
"function execute((bytes data, address to, uint256 value)[] calldata calls) external payable",
107-
// authorizationList: [signedAuthorization],
108-
params: [
109-
[
110-
{
111-
data: await encode(claimTx),
112-
to: tokenContract.address,
113-
value: 0n,
114-
},
115-
],
116-
],
117-
});
118-
119-
const batchSendResult = await sendAndConfirmTransaction({
120-
account: executor,
121-
transaction: batchSend,
122-
});
123-
console.log("BATCH SEND RESULT", batchSendResult.transactionHash);
124-
125-
expect(batchSendResult.transactionHash.length).toBe(66);
126-
127-
console.log(
128-
"BALANCE AFTER",
129-
await getWalletBalance({
130-
address: account.address,
131-
chain,
132-
client: TEST_CLIENT,
133-
tokenAddress: tokenContract.address,
134-
}),
135-
);
136-
});
137-
13831
it("should add transaction to session", async () => {
13932
const transaction = prepareTransaction({
14033
chain: ANVIL_CHAIN,

packages/thirdweb/src/transaction/actions/sign-transaction.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ export function signTransaction({
3535
privateKey,
3636
}: SignTransactionOptions): Hex {
3737
const serializedTransaction = serializeTransaction({ transaction });
38-
console.log("serializedTransaction", serializedTransaction);
3938

4039
const signature = ox__Secp256k1.sign({
4140
payload: ox__Hash.keccak256(serializedTransaction),

0 commit comments

Comments
 (0)