From 8d493ca032116c11f92a8656abed60c97bac56e5 Mon Sep 17 00:00:00 2001 From: gregfromstl Date: Wed, 1 Jan 2025 22:27:34 -1000 Subject: [PATCH] feat(sdk): simplified EIP-7702 support --- .changeset/clever-beds-knock.md | 44 ++---- packages/thirdweb/src/exports/thirdweb.ts | 9 ++ packages/thirdweb/src/exports/transaction.ts | 9 ++ .../actions/eip7702/authorization.test.ts | 27 ++++ .../actions/eip7702/authorization.ts | 48 +++++-- .../src/transaction/actions/estimate-gas.ts | 38 +++--- .../actions/send-transaction.test.ts | 109 +-------------- .../transaction/actions/sign-transaction.ts | 1 - .../to-serializable-transaction.test.ts | 125 ++++-------------- .../actions/to-serializable-transaction.ts | 92 +------------ .../transaction/prepare-transaction.test.ts | 28 ++++ .../src/transaction/prepare-transaction.ts | 6 +- .../src/utils/signatures/sign-message.test.ts | 8 +- .../src/utils/signatures/sign-message.ts | 4 +- .../thirdweb/src/wallets/interfaces/wallet.ts | 4 +- packages/thirdweb/src/wallets/private-key.ts | 4 +- 16 files changed, 184 insertions(+), 372 deletions(-) create mode 100644 packages/thirdweb/src/transaction/actions/eip7702/authorization.test.ts diff --git a/.changeset/clever-beds-knock.md b/.changeset/clever-beds-knock.md index 8926fa03242..b05ff0e1da2 100644 --- a/.changeset/clever-beds-knock.md +++ b/.changeset/clever-beds-knock.md @@ -5,20 +5,23 @@ Feature: Adds beta support for EIP-7702 authorization lists ```ts -import { prepareTransaction, sendTransaction } from "thirdweb"; +import { prepareTransaction, sendTransaction, signAuthorization } from "thirdweb"; + +const authorization = await signAuthorization({ + request: { + address: "0x...", + chainId: 911867, + nonce: 100n, + }, + account: myAccount, +}); const transaction = prepareTransaction({ chain: ANVIL_CHAIN, client: TEST_CLIENT, value: 100n, to: TEST_WALLET_B, - authorizations: [ - { - address: "0x...", - chainId: 1, - nonce: 420n, - }, - ], + authorizationList: authorization, }); const res = await sendTransaction({ @@ -27,28 +30,3 @@ const res = await sendTransaction({ }); ``` -You can access the underlying authorization signing functions like so: - -```ts -import { signAuthorization, signedAuthorizations } from "thirdweb"; - -const signedAuthorization = await signAuthorization({ - authorization: { - address: "0x...", - chainId: 1, - nonce: 420n, - }, - account: myAccount, -}); - -const signedAuthorizations = await signedAuthorizations({ - authorizations: [ - { - address: "0x...", - chainId: 1, - nonce: 420n, - }, - ], - account: myAccount, -}); -``` diff --git a/packages/thirdweb/src/exports/thirdweb.ts b/packages/thirdweb/src/exports/thirdweb.ts index ff6ce7e4e17..514625e708f 100644 --- a/packages/thirdweb/src/exports/thirdweb.ts +++ b/packages/thirdweb/src/exports/thirdweb.ts @@ -302,3 +302,12 @@ export { type VerifyTypedDataParams, verifyTypedData, } from "../auth/verify-typed-data.js"; + +/** + * EIP-7702 + */ +export type { + AuthorizationRequest, + SignedAuthorization, +} from "../transaction/actions/eip7702/authorization.js"; +export { signAuthorization } from "../transaction/actions/eip7702/authorization.js"; diff --git a/packages/thirdweb/src/exports/transaction.ts b/packages/thirdweb/src/exports/transaction.ts index 85b4229590b..18d649a2bdc 100644 --- a/packages/thirdweb/src/exports/transaction.ts +++ b/packages/thirdweb/src/exports/transaction.ts @@ -78,3 +78,12 @@ export type { GaslessOptions } from "../transaction/actions/gasless/types.js"; export type { EngineOptions } from "../transaction/actions/gasless/providers/engine.js"; export type { OpenZeppelinOptions } from "../transaction/actions/gasless/providers/openzeppelin.js"; export type { BiconomyOptions } from "../transaction/actions/gasless/providers/biconomy.js"; + +/** + * EIP-7702 + */ +export type { + AuthorizationRequest, + SignedAuthorization, +} from "../transaction/actions/eip7702/authorization.js"; +export { signAuthorization } from "../transaction/actions/eip7702/authorization.js"; diff --git a/packages/thirdweb/src/transaction/actions/eip7702/authorization.test.ts b/packages/thirdweb/src/transaction/actions/eip7702/authorization.test.ts new file mode 100644 index 00000000000..f217f330c07 --- /dev/null +++ b/packages/thirdweb/src/transaction/actions/eip7702/authorization.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; +import { TEST_WALLET_B } from "~test/addresses.js"; +import { TEST_ACCOUNT_A } from "~test/test-wallets.js"; +import { signAuthorization } from "./authorization.js"; + +describe("signAuthorization", () => { + it("should sign an authorization", async () => { + const authorization = await signAuthorization({ + account: TEST_ACCOUNT_A, + request: { + address: TEST_WALLET_B, + chainId: 911867, + nonce: 0n, + }, + }); + expect(authorization).toMatchInlineSnapshot(` + { + "address": "0x0000000000000000000000000000000000000002", + "chainId": 911867, + "nonce": 0n, + "r": 3720526934953059641417422884731844424204826752871127418111522219225437830766n, + "s": 23451045058292828843243765241045958975073226494910356096978666517928790374894n, + "yParity": 1, + } + `); + }); +}); diff --git a/packages/thirdweb/src/transaction/actions/eip7702/authorization.ts b/packages/thirdweb/src/transaction/actions/eip7702/authorization.ts index 4133ea17dd0..d39ba1696bf 100644 --- a/packages/thirdweb/src/transaction/actions/eip7702/authorization.ts +++ b/packages/thirdweb/src/transaction/actions/eip7702/authorization.ts @@ -1,34 +1,58 @@ import type * as ox__Authorization from "ox/Authorization"; import type { Address } from "../../../utils/address.js"; +import type { Account } from "../../../wallets/interfaces/wallet.js"; /** - * An EIP-7702 authorization input object. + * An EIP-7702 authorization object fully prepared and ready for signing. * * @beta * @transaction */ -export type Authorization = { +export type AuthorizationRequest = { address: Address; - chainId?: number; - nonce?: bigint; + chainId: number; + nonce: bigint; }; /** - * An EIP-7702 authorization object fully prepared and ready for signing. + * Represents a signed EIP-7702 authorization object. * * @beta * @transaction */ -export type PreparedAuthorization = { - address: Address; - chainId: number; - nonce: bigint; -}; +export type SignedAuthorization = ox__Authorization.ListSigned[number]; /** - * Represents a signed EIP-7702 authorization object. + * Sign the given EIP-7702 authorization object. + * @param options - The options for `signAuthorization` + * Refer to the type [`SignAuthorizationOptions`](https://portal.thirdweb.com/references/typescript/v5/SignAuthorizationOptions) + * @returns The signed authorization object + * + * ```ts + * import { signAuthorization } from "thirdweb"; + * + * const authorization = await signAuthorization({ + * request: { + * address: "0x...", + * chainId: 911867, + * nonce: 100n, + * }, + * account: myAccount, + * }); + * ``` * * @beta * @transaction */ -export type SignedAuthorization = ox__Authorization.ListSigned[number]; +export async function signAuthorization(options: { + account: Account; + request: AuthorizationRequest; +}): Promise { + const { account, request } = options; + if (typeof account.signAuthorization === "undefined") { + throw new Error( + "This account type does not yet support signing EIP-7702 authorizations", + ); + } + return account.signAuthorization(request); +} diff --git a/packages/thirdweb/src/transaction/actions/estimate-gas.ts b/packages/thirdweb/src/transaction/actions/estimate-gas.ts index 00ee3713818..da590d2dc95 100644 --- a/packages/thirdweb/src/transaction/actions/estimate-gas.ts +++ b/packages/thirdweb/src/transaction/actions/estimate-gas.ts @@ -1,3 +1,4 @@ +import * as ox__Hex from "ox/Hex"; import { formatTransactionRequest } from "viem"; import { roundUpGas } from "../../gas/op-gas-fee-reducer.js"; import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value.js"; @@ -5,8 +6,6 @@ import type { Prettify } from "../../utils/type-utils.js"; import type { Account } from "../../wallets/interfaces/wallet.js"; import { extractError } from "../extract-error.js"; import type { PreparedTransaction } from "../prepare-transaction.js"; -import { resolveAndSignAuthorizations } from "./to-serializable-transaction.js"; -import * as ox__Hex from "ox/Hex"; export type EstimateGasOptions = Prettify< { @@ -18,21 +17,21 @@ export type EstimateGasOptions = Prettify< transaction: PreparedTransaction; } & ( | { - /** - * The account the transaction would be sent from. - * - * @deprecated Use `from` instead - */ - account: Account; - from?: never; - } + /** + * The account the transaction would be sent from. + * + * @deprecated Use `from` instead + */ + account: Account; + from?: never; + } | { - account?: never; - /** - * The address the transaction would be sent from. - */ - from?: string | Account; - } + account?: never; + /** + * The address the transaction would be sent from. + */ + from?: string | Account; + } ) >; @@ -66,8 +65,8 @@ export async function estimateGas( // 3. the passed in wallet's account address const fromAddress = typeof options.from === "string" - ? options.from ?? undefined - : options.from?.address ?? options.account?.address; + ? (options.from ?? undefined) + : (options.from?.address ?? options.account?.address); const txWithFrom = { ...options.transaction, from: fromAddress }; if (cache.has(txWithFrom)) { // biome-ignore lint/style/noNonNullAssertion: the `has` above ensures that this will always be set @@ -104,7 +103,7 @@ export async function estimateGas( encode(options.transaction), resolvePromisedValue(options.transaction.to), resolvePromisedValue(options.transaction.value), - resolveAndSignAuthorizations(options), + resolvePromisedValue(options.transaction.authorizationList), ]); // load up the rpc client and the estimateGas function if we need it @@ -132,6 +131,7 @@ export async function estimateGas( })), }), ); + if (options.transaction.chain.experimental?.increaseZeroByteCount) { gas = roundUpGas(gas); } diff --git a/packages/thirdweb/src/transaction/actions/send-transaction.test.ts b/packages/thirdweb/src/transaction/actions/send-transaction.test.ts index b2c8406f98b..49ffa4f0e40 100644 --- a/packages/thirdweb/src/transaction/actions/send-transaction.test.ts +++ b/packages/thirdweb/src/transaction/actions/send-transaction.test.ts @@ -2,19 +2,9 @@ import { describe, expect, it, vi } from "vitest"; import { TEST_WALLET_B } from "../../../test/src/addresses.js"; import { ANVIL_CHAIN } from "../../../test/src/chains.js"; import { TEST_CLIENT } from "../../../test/src/test-clients.js"; -import { - TEST_ACCOUNT_A, - TEST_ACCOUNT_B, -} from "../../../test/src/test-wallets.js"; -import { defineChain } from "../../chains/utils.js"; -import { getContract } from "../../contract/contract.js"; -import { claimTo } from "../../extensions/erc20/drops/write/claimTo.js"; -import { getWalletBalance } from "../../wallets/utils/getWalletBalance.js"; -import { prepareContractCall } from "../prepare-contract-call.js"; +import { TEST_ACCOUNT_A } from "../../../test/src/test-wallets.js"; import { prepareTransaction } from "../prepare-transaction.js"; import * as TransactionStore from "../transaction-store.js"; -import { encode } from "./encode.js"; -import { sendAndConfirmTransaction } from "./send-and-confirm-transaction.js"; import { sendTransaction } from "./send-transaction.js"; const addTransactionToStore = vi.spyOn( @@ -38,103 +28,6 @@ describe("sendTransaction", () => { expect(res.transactionHash.length).toBe(66); }); - it.only("should send an eip7702 transaction", async () => { - const account = TEST_ACCOUNT_A; - const executor = TEST_ACCOUNT_B; - - console.log("ACCOUNT", account.address); - console.log("DELEGATE", executor.address); - - const chain = defineChain(911867); - const tokenContract = getContract({ - address: "0xAA462a5BE0fc5214507FDB4fB2474a7d5c69065b", - chain, - client: TEST_CLIENT, - }); - - const claimTx = claimTo({ - contract: tokenContract, - quantityInWei: 1000n, - to: account.address, - }); - - // const transferTx = transfer({ - // contract: tokenContract, - // amountWei: 1000n, - // to: "0x2247d5d238d0f9d37184d8332aE0289d1aD9991b", - // }); - - console.log( - "BALANCE BEFORE", - await getWalletBalance({ - address: account.address, - chain, - client: TEST_CLIENT, - tokenAddress: tokenContract.address, - }), - ); - - // const nonce = await (async () => { - // const rpcRequest = getRpcClient({ - // chain, - // client: TEST_CLIENT, - // }); - // const { eth_getTransactionCount } = await import( - // "../../rpc/actions/eth_getTransactionCount.js" - // ); - // return await eth_getTransactionCount(rpcRequest, { - // address: account.address, - // blockTag: "pending", - // }); - // })(); - - // const signedAuthorization = await account.signAuthorization!({ - // address: "0x654F42b74885EE6803F403f077bc0409f1066c58", - // chainId: chain.id, - // nonce: BigInt(nonce), - // }); - - // console.log("SIGNED AUTHORIZATION", signedAuthorization); - - const batchSend = prepareContractCall({ - contract: getContract({ - address: account.address, - chain, - client: TEST_CLIENT, - }), - method: - "function execute((bytes data, address to, uint256 value)[] calldata calls) external payable", - // authorizationList: [signedAuthorization], - params: [ - [ - { - data: await encode(claimTx), - to: tokenContract.address, - value: 0n, - }, - ], - ], - }); - - const batchSendResult = await sendAndConfirmTransaction({ - account: executor, - transaction: batchSend, - }); - console.log("BATCH SEND RESULT", batchSendResult.transactionHash); - - expect(batchSendResult.transactionHash.length).toBe(66); - - console.log( - "BALANCE AFTER", - await getWalletBalance({ - address: account.address, - chain, - client: TEST_CLIENT, - tokenAddress: tokenContract.address, - }), - ); - }); - it("should add transaction to session", async () => { const transaction = prepareTransaction({ chain: ANVIL_CHAIN, diff --git a/packages/thirdweb/src/transaction/actions/sign-transaction.ts b/packages/thirdweb/src/transaction/actions/sign-transaction.ts index bec8e7bb732..efabb96d947 100644 --- a/packages/thirdweb/src/transaction/actions/sign-transaction.ts +++ b/packages/thirdweb/src/transaction/actions/sign-transaction.ts @@ -35,7 +35,6 @@ export function signTransaction({ privateKey, }: SignTransactionOptions): Hex { const serializedTransaction = serializeTransaction({ transaction }); - console.log("serializedTransaction", serializedTransaction); const signature = ox__Secp256k1.sign({ payload: ox__Hash.keccak256(serializedTransaction), diff --git a/packages/thirdweb/src/transaction/actions/to-serializable-transaction.test.ts b/packages/thirdweb/src/transaction/actions/to-serializable-transaction.test.ts index 8c6734fd699..c2edee102f4 100644 --- a/packages/thirdweb/src/transaction/actions/to-serializable-transaction.test.ts +++ b/packages/thirdweb/src/transaction/actions/to-serializable-transaction.test.ts @@ -12,6 +12,7 @@ import { } from "../prepare-transaction.js"; import { serializeTransaction } from "../serialize-transaction.js"; import { toSerializableTransaction } from "./to-serializable-transaction.js"; +import { signAuthorization } from "./eip7702/authorization.js"; describe.runIf(process.env.TW_SECRET_KEY)("toSerializableTransaction", () => { let transaction: PreparedTransaction; @@ -275,16 +276,19 @@ describe.runIf(process.env.TW_SECRET_KEY)("toSerializableTransaction", () => { describe("authorizations", () => { test("should be able to be set", async () => { + const authorization = await signAuthorization({ + account: TEST_ACCOUNT_A, + request: { + address: TEST_WALLET_B, + chainId: 1, + nonce: 100n, + }, + }); + const serializableTransaction = await toSerializableTransaction({ transaction: { ...transaction, - authorizations: [ - { - address: TEST_WALLET_B, - chainId: 1, - nonce: 420n, - }, - ], + authorizationList: [authorization], }, from: TEST_ACCOUNT_A, }); @@ -294,27 +298,29 @@ describe.runIf(process.env.TW_SECRET_KEY)("toSerializableTransaction", () => { { "address": "0x0000000000000000000000000000000000000002", "chainId": 1, - "nonce": 420n, - "r": 106662916142344786844727900295567797819003816901437860515446079683830339692340n, - "s": 46957778264629617940565302067995657621333912569294987161175737034959018506312n, - "yParity": 0, + "nonce": 100n, + "r": 80806665504145908662094143605220407474886149466352261863122583017203514896219n, + "s": 35406481756212480507222011619049260135807579374282360733409834151386668114999n, + "yParity": 1, }, ] `); }); test("should be able to be a promised value", async () => { + const authorization = await signAuthorization({ + account: TEST_ACCOUNT_A, + request: { + address: TEST_WALLET_B, + chainId: 1, + nonce: 100n, + }, + }); + const serializableTransaction = await toSerializableTransaction({ transaction: { ...transaction, - authorizations: async () => - Promise.resolve([ - { - address: TEST_WALLET_B, - chainId: 1, - nonce: 420n, - }, - ]), + authorizationList: async () => Promise.resolve([authorization]), }, from: TEST_ACCOUNT_A, }); @@ -324,87 +330,14 @@ describe.runIf(process.env.TW_SECRET_KEY)("toSerializableTransaction", () => { { "address": "0x0000000000000000000000000000000000000002", "chainId": 1, - "nonce": 420n, - "r": 106662916142344786844727900295567797819003816901437860515446079683830339692340n, - "s": 46957778264629617940565302067995657621333912569294987161175737034959018506312n, - "yParity": 0, + "nonce": 100n, + "r": 80806665504145908662094143605220407474886149466352261863122583017203514896219n, + "s": 35406481756212480507222011619049260135807579374282360733409834151386668114999n, + "yParity": 1, }, ] `); }); - - test("should fill in `chainId` and `nonce` for authorizations if none are provided", async () => { - const tx = await toSerializableTransaction({ - transaction: { - ...transaction, - authorizations: [ - { - address: TEST_WALLET_B, - }, - ], - }, - from: TEST_ACCOUNT_A, - }); - - expect(tx.authorizationList?.[0]?.chainId).toBe(transaction.chain.id); - expect(tx.authorizationList?.[0]?.nonce).toBe(BigInt(tx.nonce ?? 0)); - }); - - test("should set `to` to the first authorization address if `to` is not provided for an authorization transaction", async () => { - const tx = await toSerializableTransaction({ - transaction: { - ...transaction, - to: undefined, - authorizations: [ - { - address: TEST_WALLET_B, - chainId: 1, - nonce: 420n, - }, - ], - }, - from: TEST_ACCOUNT_A, - }); - - expect(tx.to).toBe(TEST_WALLET_B); - }); - - test("should throw if no account is provided", async () => { - await expect( - toSerializableTransaction({ - transaction: { - ...transaction, - authorizations: [ - { - address: TEST_WALLET_B, - chainId: 1, - nonce: 420n, - }, - ], - }, - }), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [Error: toSerializableTransaction - This transaction has authorizations specified, please provide an account to sign them.] - `); - - await expect( - toSerializableTransaction({ - transaction: { - ...transaction, - authorizations: [ - { - address: TEST_WALLET_B, - chainId: 1, - nonce: 420n, - }, - ], - }, - from: TEST_WALLET_B, - }), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [Error: toSerializableTransaction - This transaction has authorizations specified, please provide an account to sign them.] - `); - }); }); describe("extraGas override", () => { diff --git a/packages/thirdweb/src/transaction/actions/to-serializable-transaction.ts b/packages/thirdweb/src/transaction/actions/to-serializable-transaction.ts index 990b2c71d04..fc7bc2b66cd 100644 --- a/packages/thirdweb/src/transaction/actions/to-serializable-transaction.ts +++ b/packages/thirdweb/src/transaction/actions/to-serializable-transaction.ts @@ -6,10 +6,6 @@ import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value import type { Account } from "../../wallets/interfaces/wallet.js"; import type { PreparedTransaction } from "../prepare-transaction.js"; import type { SerializableTransaction } from "../serialize-transaction.js"; -import type { - PreparedAuthorization, - SignedAuthorization, -} from "./eip7702/authorization.js"; import { encode } from "./encode.js"; import { estimateGas } from "./estimate-gas.js"; @@ -110,14 +106,9 @@ export async function toSerializableTransaction( resolvePromisedValue(options.transaction.to), resolvePromisedValue(options.transaction.accessList), resolvePromisedValue(options.transaction.value), - resolveAndSignAuthorizations(options), + resolvePromisedValue(options.transaction.authorizationList), ]); - // If this is an EIP-7702 transaction, we default the `to` address to the sender themselves as they are authorizing their EOA to point to the contract - if (typeof authorizationList !== "undefined" && typeof to === "undefined") { - to = typeof from === "string" ? from : from?.address; - } - const extraGas = await resolvePromisedValue(options.transaction.extraGas); if (extraGas) { gas += extraGas; @@ -131,86 +122,7 @@ export async function toSerializableTransaction( nonce, accessList, value, - ...feeData, authorizationList, + ...feeData, } satisfies SerializableTransaction; } - -/** - * Helper function to resolve and sign authorizations if they are present. - * @internal - */ -export async function resolveAndSignAuthorizations( - options: ToSerializableTransactionOptions, -): Promise { - if (typeof options.transaction.authorizationList !== "undefined") { - return await resolvePromisedValue(options.transaction.authorizationList); - } - - if (typeof options.transaction.authorizations === "undefined") { - return undefined; - } - - const account = (() => { - if ( - typeof options.from === "string" || - typeof options.from === "undefined" - ) { - throw new Error( - "This transaction has authorizations specified, please provide an account to sign them.", - ); - } - return options.from; - })(); - - const authorizations = await resolvePromisedValue( - options.transaction.authorizations, - ); - - if (typeof authorizations === "undefined") { - return undefined; - } - - if (authorizations.length === 0) { - return []; - } - - if (typeof account.signAuthorization === "undefined") { - throw new Error( - "This account does not support signing EIP-7702 authorizations.", - ); - } - - const startingNonce = authorizations.some( - (a) => typeof a.nonce === "undefined", - ) - ? await (async () => { - const nonce = await resolvePromisedValue(options.transaction.nonce); - if (typeof nonce !== "undefined") { - return nonce; - } - - const rpcRequest = getRpcClient(options.transaction); - const { eth_getTransactionCount } = await import( - "../../rpc/actions/eth_getTransactionCount.js" - ); - return await eth_getTransactionCount(rpcRequest, { - address: account.address, - blockTag: "pending", - }); - })() - : 0; - - const formattedAuthorizations: PreparedAuthorization[] = authorizations.map( - (authorization, idx) => { - return { - ...authorization, - // Use provided values if available, otherwise fallback to the transaction values - chainId: authorization.chainId ?? options.transaction.chain.id, - nonce: authorization.nonce ?? BigInt(startingNonce + idx + 1), - }; - }, - ); - - return Promise.all(formattedAuthorizations.map(account.signAuthorization)); -} diff --git a/packages/thirdweb/src/transaction/prepare-transaction.test.ts b/packages/thirdweb/src/transaction/prepare-transaction.test.ts index 3acc2ef25c3..26c676b1f4f 100644 --- a/packages/thirdweb/src/transaction/prepare-transaction.test.ts +++ b/packages/thirdweb/src/transaction/prepare-transaction.test.ts @@ -1,9 +1,13 @@ import { describe, expect, test as it } from "vitest"; +import { TEST_ACCOUNT_B } from "~test/test-wallets.js"; import { TEST_WALLET_A, TEST_WALLET_B } from "../../test/src/addresses.js"; import { FORKED_ETHEREUM_CHAIN } from "../../test/src/chains.js"; import { TEST_CLIENT } from "../../test/src/test-clients.js"; +import { defineChain } from "../chains/utils.js"; import { toWei } from "../utils/units.js"; +import { signAuthorization } from "./actions/eip7702/authorization.js"; import { estimateGas } from "./actions/estimate-gas.js"; +import { toSerializableTransaction } from "./actions/to-serializable-transaction.js"; import { prepareTransaction } from "./prepare-transaction.js"; describe.runIf(process.env.TW_SECRET_KEY)("prepareTransaction", () => { @@ -18,6 +22,30 @@ describe.runIf(process.env.TW_SECRET_KEY)("prepareTransaction", () => { expect(preparedTx.value).toMatchInlineSnapshot("100000000000000000n"); }); + it("should accept an authorization list", async () => { + const authorization = await signAuthorization({ + account: TEST_ACCOUNT_B, + request: { + address: TEST_WALLET_B, + chainId: 911867, + nonce: 0n, + }, + }); + const preparedTx = prepareTransaction({ + chain: defineChain(911867), + client: TEST_CLIENT, + to: TEST_WALLET_B, + value: 0n, + authorizationList: [authorization], + }); + + const serializableTx = await toSerializableTransaction({ + transaction: preparedTx, + }); + + expect(serializableTx.authorizationList).toEqual([authorization]); + }); + // skip this test if there is no secret key available to test with // TODO: remove reliance on secret key during unit tests entirely it.runIf(process.env.TW_SECRET_KEY)( diff --git a/packages/thirdweb/src/transaction/prepare-transaction.ts b/packages/thirdweb/src/transaction/prepare-transaction.ts index 4f6e4361843..aef9a876a82 100644 --- a/packages/thirdweb/src/transaction/prepare-transaction.ts +++ b/packages/thirdweb/src/transaction/prepare-transaction.ts @@ -5,10 +5,7 @@ import type { ThirdwebClient } from "../client/client.js"; import type { ThirdwebContract } from "../contract/contract.js"; import type { PreparedMethod } from "../utils/abi/prepare-method.js"; import type { PromisedObject } from "../utils/promise/resolve-promised-value.js"; -import type { - Authorization, - SignedAuthorization, -} from "./actions/eip7702/authorization.js"; +import type { SignedAuthorization } from "./actions/eip7702/authorization.js"; export type StaticPrepareTransactionOptions = { accessList?: AccessList | undefined; @@ -23,7 +20,6 @@ export type StaticPrepareTransactionOptions = { nonce?: number | undefined; extraGas?: bigint | undefined; // eip7702 - authorizations?: Authorization[] | undefined; authorizationList?: SignedAuthorization[] | undefined; // zksync specific eip712?: EIP712TransactionOptions | undefined; diff --git a/packages/thirdweb/src/utils/signatures/sign-message.test.ts b/packages/thirdweb/src/utils/signatures/sign-message.test.ts index 62276ac4ce6..34718acd684 100644 --- a/packages/thirdweb/src/utils/signatures/sign-message.test.ts +++ b/packages/thirdweb/src/utils/signatures/sign-message.test.ts @@ -39,7 +39,9 @@ describe("signMessage", async () => { message: { raw: "0x68656c6c6f20776f726c64" }, privateKey: ANVIL_PKEY_A, }), - ).toMatchInlineSnapshot(`"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"`); + ).toMatchInlineSnapshot( + `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"`, + ); expect( signMessage({ @@ -50,7 +52,9 @@ describe("signMessage", async () => { }, privateKey: ANVIL_PKEY_A, }), - ).toMatchInlineSnapshot(`"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"`); + ).toMatchInlineSnapshot( + `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"`, + ); }); test("account", async () => { diff --git a/packages/thirdweb/src/utils/signatures/sign-message.ts b/packages/thirdweb/src/utils/signatures/sign-message.ts index 116e86d7341..18a461975d0 100644 --- a/packages/thirdweb/src/utils/signatures/sign-message.ts +++ b/packages/thirdweb/src/utils/signatures/sign-message.ts @@ -9,8 +9,8 @@ import type { Prettify } from "../type-utils.js"; type Message = Prettify< | string | { - raw: Hex | Uint8Array; - } + raw: Hex | Uint8Array; + } >; export type SignMessageOptions = { message: Message; diff --git a/packages/thirdweb/src/wallets/interfaces/wallet.ts b/packages/thirdweb/src/wallets/interfaces/wallet.ts index 2415e4b9184..09d57d76e9b 100644 --- a/packages/thirdweb/src/wallets/interfaces/wallet.ts +++ b/packages/thirdweb/src/wallets/interfaces/wallet.ts @@ -3,7 +3,7 @@ import type * as ox__TypedData from "ox/TypedData"; import type { Hex, SignableMessage } from "viem"; import type { Chain } from "../../chains/types.js"; import type { - PreparedAuthorization, + AuthorizationRequest, SignedAuthorization, } from "../../transaction/actions/eip7702/authorization.js"; import type { @@ -207,7 +207,7 @@ export type Account = { * ``` */ signAuthorization?: ( - authorization: PreparedAuthorization, + authorization: AuthorizationRequest, ) => Promise; /** diff --git a/packages/thirdweb/src/wallets/private-key.ts b/packages/thirdweb/src/wallets/private-key.ts index ebd02abe5e2..efd8565b6cd 100644 --- a/packages/thirdweb/src/wallets/private-key.ts +++ b/packages/thirdweb/src/wallets/private-key.ts @@ -7,7 +7,7 @@ import { getCachedChain } from "../chains/utils.js"; import type { ThirdwebClient } from "../client/client.js"; import { eth_sendRawTransaction } from "../rpc/actions/eth_sendRawTransaction.js"; import { getRpcClient } from "../rpc/rpc.js"; -import type { PreparedAuthorization } from "../transaction/actions/eip7702/authorization.js"; +import type { AuthorizationRequest } from "../transaction/actions/eip7702/authorization.js"; import { signTransaction } from "../transaction/actions/sign-transaction.js"; import type { SerializableTransaction } from "../transaction/serialize-transaction.js"; import { type Hex, toHex } from "../utils/encoding/hex.js"; @@ -122,7 +122,7 @@ export function privateKeyToAccount( privateKey, }); }, - signAuthorization: async (authorization: PreparedAuthorization) => { + signAuthorization: async (authorization: AuthorizationRequest) => { const signature = ox__Secp256k1.sign({ payload: ox__Authorization.getSignPayload(authorization), privateKey: privateKey,