From 5ab60e37617e6296e93319f6ffda4b0783d1a853 Mon Sep 17 00:00:00 2001 From: jxom <7336481+jxom@users.noreply.github.com> Date: Tue, 26 Nov 2024 07:03:25 +0100 Subject: [PATCH] feat: `prepareAuthorization` (#3062) * feat: add `prepareAuthorization` * chore: format * Update rotten-camels-knock.md * chore: up snapshots --- .changeset/rotten-camels-knock.md | 5 + .../eip7702/prepareAuthorization.md | 179 ++++++++++++++++++ site/sidebar.ts | 4 + .../actions/prepareAuthorization.test.ts | 174 +++++++++++++++++ .../eip7702/actions/prepareAuthorization.ts | 141 ++++++++++++++ .../eip7702/actions/signAuthorization.ts | 69 +------ .../eip7702/decorators/eip7702.test.ts | 1 + .../eip7702/decorators/eip7702.ts | 53 ++++++ src/experimental/index.ts | 6 + 9 files changed, 572 insertions(+), 60 deletions(-) create mode 100644 .changeset/rotten-camels-knock.md create mode 100644 site/pages/experimental/eip7702/prepareAuthorization.md create mode 100644 src/experimental/eip7702/actions/prepareAuthorization.test.ts create mode 100644 src/experimental/eip7702/actions/prepareAuthorization.ts diff --git a/.changeset/rotten-camels-knock.md b/.changeset/rotten-camels-knock.md new file mode 100644 index 0000000000..8eea44ea01 --- /dev/null +++ b/.changeset/rotten-camels-knock.md @@ -0,0 +1,5 @@ +--- +"viem": patch +--- + +**Experimental (EIP-7702):** Added `prepareAuthorization`. diff --git a/site/pages/experimental/eip7702/prepareAuthorization.md b/site/pages/experimental/eip7702/prepareAuthorization.md new file mode 100644 index 0000000000..83775751a8 --- /dev/null +++ b/site/pages/experimental/eip7702/prepareAuthorization.md @@ -0,0 +1,179 @@ +--- +description: Prepares an EIP-7702 Authorization for signing. +--- + +# prepareAuthorization + +Prepares an [EIP-7702 Authorization](https://eips.ethereum.org/EIPS/eip-7702) for signing. +This Action will fill the required fields of the Authorization object if they are not provided (e.g. `nonce` and `chainId`). + +With the prepared Authorization object, you can use [`signAuthorization`](/experimental/eip7702/signAuthorization) to sign over it. + +## Usage + +:::code-group + +```ts twoslash [example.ts] +import { walletClient } from './client' + +const authorization = await walletClient.prepareAuthorization({ // [!code focus] + contractAddress: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus] +}) // [!code focus] +// @log: { +// @log: chainId: 1, +// @log: contractAddress: "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", +// @log: nonce: 1, +// @log: } + +const signedAuthorization = await walletClient.signAuthorization(authorization) +``` + +```ts twoslash [client.ts] filename="client.ts" +import { createWalletClient, http } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { mainnet } from 'viem/chains' +import { eip7702Actions } from 'viem/experimental' + +export const walletClient = createWalletClient({ + account: privateKeyToAccount('0x...'), + chain: mainnet, + transport: http(), +}).extend(eip7702Actions()) +``` + +::: + +### Explicit Scoping + +We can explicitly set a `nonce` and/or `chainId` by supplying them as parameters: + +:::code-group + +```ts twoslash [example.ts] +import { walletClient } from './client' + +const authorization = await walletClient.prepareAuthorization({ + contractAddress: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', + chainId: 10, // [!code focus] +}) +// @log: { +// @log: chainId: 10, +// @log: contractAddress: "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", +// @log: nonce: 420, +// @log: } + +const signedAuthorization = await walletClient.signAuthorization(authorization) +``` + +```ts twoslash [client.ts] filename="client.ts" +import { createWalletClient, http } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { mainnet } from 'viem/chains' +import { eip7702Actions } from 'viem/experimental' + +export const walletClient = createWalletClient({ + account: privateKeyToAccount('0x...'), + chain: mainnet, + transport: http(), +}).extend(eip7702Actions()) +``` + +::: + +## Returns + +`Authorization` + +A prepared & unsigned Authorization object. + +## Parameters + +### account + +- **Type:** `Account` + +Account to use to prepare the Authorization object. + +Accepts a [Local Account (Private Key, etc)](/docs/clients/wallet#local-accounts-private-key-mnemonic-etc). + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { walletClient } from './client' + +const authorization = await walletClient.prepareAuthorization({ + account: privateKeyToAccount('0x...'), // [!code focus] + contractAddress: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2' +}) +``` + +### chainId (optional) + +- **Type:** `Address` +- **Default:** `client.chain.id` or Network chain ID + +The Chain ID to scope the Authorization to. If set to zero (`0`), then the Authorization will +be valid on all chains. + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { walletClient } from './client' + +const authorization = await walletClient.prepareAuthorization({ + account: privateKeyToAccount('0x...'), + contractAddress: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', + chainId: 1, // [!code focus] +}) +``` + +### contractAddress + +- **Type:** `Address` + +The target Contract to designate onto the Account. + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { walletClient } from './client' + +const authorization = await walletClient.prepareAuthorization({ + account: privateKeyToAccount('0x...'), + contractAddress: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2' // [!code focus] +}) +``` + +### delegate (optional) + +- **Type:** `true | Address | Account` + +Whether the EIP-7702 Transaction will be executed by another Account. + +If not specified, it will be assumed that the EIP-7702 Transaction will be executed by the Account that signed the Authorization. + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { walletClient } from './client' + +const authorization = await walletClient.prepareAuthorization({ + account: privateKeyToAccount('0x...'), + contractAddress: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', + delegate: true, // [!code focus] +}) +``` + +### nonce (optional) + +- **Type:** `Address` +- **Default:** Account's next available nonce. + +The nonce to scope the Authorization to. + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { walletClient } from './client' + +const authorization = await walletClient.prepareAuthorization({ + account: privateKeyToAccount('0x...'), + contractAddress: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', + nonce: 69, // [!code focus] +}) +``` \ No newline at end of file diff --git a/site/sidebar.ts b/site/sidebar.ts index 0683caac44..13454085fa 100644 --- a/site/sidebar.ts +++ b/site/sidebar.ts @@ -1301,6 +1301,10 @@ export const sidebar = { { text: 'Actions', items: [ + { + text: 'prepareAuthorization', + link: '/experimental/eip7702/prepareAuthorization', + }, { text: 'signAuthorization', link: '/experimental/eip7702/signAuthorization', diff --git a/src/experimental/eip7702/actions/prepareAuthorization.test.ts b/src/experimental/eip7702/actions/prepareAuthorization.test.ts new file mode 100644 index 0000000000..4cb8b6d6bf --- /dev/null +++ b/src/experimental/eip7702/actions/prepareAuthorization.test.ts @@ -0,0 +1,174 @@ +import { beforeAll, expect, test } from 'vitest' +import { wagmiContractConfig } from '../../../../test/src/abis.js' +import { anvilMainnet } from '../../../../test/src/anvil.js' +import { accounts } from '../../../../test/src/constants.js' +import { privateKeyToAccount } from '../../../accounts/privateKeyToAccount.js' +import { reset } from '../../../actions/index.js' +import { prepareAuthorization } from './prepareAuthorization.js' + +const account = privateKeyToAccount(accounts[0].privateKey) +const client = anvilMainnet.getClient() + +beforeAll(async () => { + await reset(client, { + blockNumber: anvilMainnet.forkBlockNumber, + jsonRpcUrl: anvilMainnet.forkUrl, + }) +}) + +test('default', async () => { + const authorization = await prepareAuthorization(client, { + account, + contractAddress: wagmiContractConfig.address, + chainId: 1, + nonce: 0, + }) + + expect(authorization).toMatchInlineSnapshot( + ` + { + "chainId": 1, + "contractAddress": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "nonce": 0, + } + `, + ) +}) + +test('behavior: partial authorization: no chainId + nonce', async () => { + const authorization = await prepareAuthorization(client, { + account, + contractAddress: wagmiContractConfig.address, + }) + + expect(authorization).toMatchInlineSnapshot( + ` + { + "chainId": 1, + "contractAddress": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "nonce": 664, + } + `, + ) +}) + +test('behavior: partial authorization: no nonce', async () => { + const authorization = await prepareAuthorization(client, { + account, + contractAddress: wagmiContractConfig.address, + chainId: 10, + }) + + expect(authorization).toMatchInlineSnapshot( + ` + { + "chainId": 10, + "contractAddress": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "nonce": 664, + } + `, + ) +}) + +test('behavior: partial authorization: no chainId', async () => { + const authorization = await prepareAuthorization(client, { + account, + contractAddress: wagmiContractConfig.address, + nonce: 69, + }) + + expect(authorization).toMatchInlineSnapshot( + ` + { + "chainId": 1, + "contractAddress": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "nonce": 69, + } + `, + ) +}) + +test('behavior: delegate is address', async () => { + const authorization = await prepareAuthorization(client, { + account, + contractAddress: wagmiContractConfig.address, + delegate: '0x0000000000000000000000000000000000000000', + }) + + expect(authorization.nonce).toBe(663) +}) + +test('behavior: delegate is truthy', async () => { + const authorization = await prepareAuthorization(client, { + account, + contractAddress: wagmiContractConfig.address, + delegate: true, + }) + + expect(authorization.nonce).toBe(663) +}) + +test('behavior: account as delegate', async () => { + const authorization = await prepareAuthorization(client, { + account, + contractAddress: wagmiContractConfig.address, + delegate: account, + }) + + expect(authorization.nonce).toBe(664) +}) + +test('behavior: hoisted account on client', async () => { + const client = anvilMainnet.getClient({ account }) + const authorization = await prepareAuthorization(client, { + contractAddress: wagmiContractConfig.address, + chainId: 1, + nonce: 0, + }) + + expect(authorization).toMatchInlineSnapshot( + ` + { + "chainId": 1, + "contractAddress": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "nonce": 0, + } + `, + ) +}) + +test('behavior: no client chain', async () => { + const client = anvilMainnet.getClient({ chain: false }) + const authorization = await prepareAuthorization(client, { + account, + contractAddress: wagmiContractConfig.address, + nonce: 0, + }) + + expect(authorization).toMatchInlineSnapshot( + ` + { + "chainId": 1, + "contractAddress": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "nonce": 0, + } + `, + ) +}) + +test('error: no account', async () => { + await expect(() => + // @ts-expect-error + prepareAuthorization(client, { + contractAddress: wagmiContractConfig.address, + chainId: 1, + nonce: 0, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [AccountNotFoundError: Could not find an Account to execute with this Action. + Please provide an Account with the \`account\` argument on the Action, or by supplying an \`account\` to the Client. + + Docs: https://viem.sh/experimental/eip7702/prepareAuthorization + Version: viem@x.y.z] + `) +}) diff --git a/src/experimental/eip7702/actions/prepareAuthorization.ts b/src/experimental/eip7702/actions/prepareAuthorization.ts new file mode 100644 index 0000000000..1420cfaaee --- /dev/null +++ b/src/experimental/eip7702/actions/prepareAuthorization.ts @@ -0,0 +1,141 @@ +import type { Address } from 'abitype' +import type { Account } from '../../../accounts/types.js' +import { + type ParseAccountErrorType, + parseAccount, +} from '../../../accounts/utils/parseAccount.js' +import { getChainId } from '../../../actions/public/getChainId.js' +import { getTransactionCount } from '../../../actions/public/getTransactionCount.js' +import type { Client } from '../../../clients/createClient.js' +import type { Transport } from '../../../clients/transports/createTransport.js' +import { + AccountNotFoundError, + type AccountNotFoundErrorType, +} from '../../../errors/account.js' +import type { ErrorType } from '../../../errors/utils.js' +import type { GetAccountParameter } from '../../../types/account.js' +import type { Chain } from '../../../types/chain.js' +import type { PartialBy } from '../../../types/utils.js' +import { isAddressEqual } from '../../../utils/address/isAddressEqual.js' +import type { RequestErrorType } from '../../../utils/buildRequest.js' +import { getAction } from '../../../utils/getAction.js' +import type { Authorization } from '../types/authorization.js' + +export type PrepareAuthorizationParameters< + account extends Account | undefined = Account | undefined, +> = GetAccountParameter & + PartialBy & { + /** + * Whether the EIP-7702 Transaction will be executed by another Account. + * + * If not specified, it will be assumed that the EIP-7702 Transaction will + * be executed by the Account that signed the Authorization. + */ + delegate?: true | Address | Account | undefined + } + +export type PrepareAuthorizationReturnType = Authorization + +export type PrepareAuthorizationErrorType = + | ParseAccountErrorType + | RequestErrorType + | AccountNotFoundErrorType + | ErrorType + +/** + * Prepares an [EIP-7702 Authorization](https://eips.ethereum.org/EIPS/eip-7702) object for signing. + * This Action will fill the required fields of the Authorization object if they are not provided (e.g. `nonce` and `chainId`). + * + * With the prepared Authorization object, you can use [`signAuthorization`](https://viem.sh/experimental/eip7702/signAuthorization) to sign over the Authorization object. + * + * @param client - Client to use + * @param parameters - {@link PrepareAuthorizationParameters} + * @returns The prepared Authorization object. {@link PrepareAuthorizationReturnType} + * + * @example + * import { createClient, http } from 'viem' + * import { privateKeyToAccount } from 'viem/accounts' + * import { mainnet } from 'viem/chains' + * import { prepareAuthorization } from 'viem/experimental' + * + * const client = createClient({ + * chain: mainnet, + * transport: http(), + * }) + * const authorization = await prepareAuthorization(client, { + * account: privateKeyToAccount('0x..'), + * contractAddress: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + * }) + * + * @example + * // Account Hoisting + * import { createClient, http } from 'viem' + * import { privateKeyToAccount } from 'viem/accounts' + * import { mainnet } from 'viem/chains' + * import { prepareAuthorization } from 'viem/experimental' + * + * const client = createClient({ + * account: privateKeyToAccount('0x…'), + * chain: mainnet, + * transport: http(), + * }) + * const authorization = await prepareAuthorization(client, { + * contractAddress: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + * }) + */ +export async function prepareAuthorization< + chain extends Chain | undefined, + account extends Account | undefined, +>( + client: Client, + parameters: PrepareAuthorizationParameters, +): Promise { + const { + account: account_ = client.account, + contractAddress, + chainId, + nonce, + delegate: delegate_, + } = parameters + + if (!account_) + throw new AccountNotFoundError({ + docsPath: '/experimental/eip7702/prepareAuthorization', + }) + const account = parseAccount(account_) + + const delegate = (() => { + if (typeof delegate_ === 'boolean') return delegate_ + if (delegate_) return parseAccount(delegate_) + return undefined + })() + + const authorization = { + contractAddress, + chainId, + nonce, + } as Authorization + + if (typeof authorization.chainId === 'undefined') + authorization.chainId = + client.chain?.id ?? + (await getAction(client, getChainId, 'getChainId')({})) + + if (typeof authorization.nonce === 'undefined') { + authorization.nonce = await getAction( + client, + getTransactionCount, + 'getTransactionCount', + )({ + address: account.address, + blockTag: 'pending', + }) + if ( + !delegate || + (delegate !== true && isAddressEqual(account.address, delegate.address)) + ) + authorization.nonce += 1 + } + + return authorization +} diff --git a/src/experimental/eip7702/actions/signAuthorization.ts b/src/experimental/eip7702/actions/signAuthorization.ts index 80fd4af5ae..1306c694f4 100644 --- a/src/experimental/eip7702/actions/signAuthorization.ts +++ b/src/experimental/eip7702/actions/signAuthorization.ts @@ -1,4 +1,3 @@ -import type { Address } from 'abitype' import type { Account } from '../../../accounts/types.js' import { type ParseAccountErrorType, @@ -8,8 +7,6 @@ import type { SignAuthorizationErrorType as SignAuthorizationErrorType_account, SignAuthorizationReturnType as SignAuthorizationReturnType_account, } from '../../../accounts/utils/signAuthorization.js' -import { getChainId } from '../../../actions/public/getChainId.js' -import { getTransactionCount } from '../../../actions/public/getTransactionCount.js' import type { Client } from '../../../clients/createClient.js' import type { Transport } from '../../../clients/transports/createTransport.js' import { @@ -19,34 +16,24 @@ import { type AccountTypeNotSupportedErrorType, } from '../../../errors/account.js' import type { ErrorType } from '../../../errors/utils.js' -import type { GetAccountParameter } from '../../../types/account.js' import type { Chain } from '../../../types/chain.js' -import type { PartialBy } from '../../../types/utils.js' -import { isAddressEqual } from '../../../utils/address/isAddressEqual.js' -import type { RequestErrorType } from '../../../utils/buildRequest.js' -import { getAction } from '../../../utils/getAction.js' -import type { Authorization } from '../types/authorization.js' +import { + type PrepareAuthorizationErrorType, + type PrepareAuthorizationParameters, + prepareAuthorization, +} from './prepareAuthorization.js' export type SignAuthorizationParameters< account extends Account | undefined = Account | undefined, -> = GetAccountParameter & - PartialBy & { - /** - * Whether the EIP-7702 Transaction will be executed by another Account. - * - * If not specified, it will be assumed that the EIP-7702 Transaction will - * be executed by the Account that signed the Authorization. - */ - delegate?: true | Address | Account | undefined - } +> = PrepareAuthorizationParameters export type SignAuthorizationReturnType = SignAuthorizationReturnType_account export type SignAuthorizationErrorType = | ParseAccountErrorType - | RequestErrorType | AccountNotFoundErrorType | AccountTypeNotSupportedErrorType + | PrepareAuthorizationErrorType | SignAuthorizationErrorType_account | ErrorType @@ -99,13 +86,7 @@ export async function signAuthorization< client: Client, parameters: SignAuthorizationParameters, ): Promise { - const { - account: account_ = client.account, - contractAddress, - chainId, - nonce, - delegate: delegate_, - } = parameters + const { account: account_ = client.account } = parameters if (!account_) throw new AccountNotFoundError({ @@ -113,12 +94,6 @@ export async function signAuthorization< }) const account = parseAccount(account_) - const delegate = (() => { - if (typeof delegate_ === 'boolean') return delegate_ - if (delegate_) return parseAccount(delegate_) - return undefined - })() - if (!account.experimental_signAuthorization) throw new AccountTypeNotSupportedError({ docsPath: '/experimental/eip7702/signAuthorization', @@ -128,32 +103,6 @@ export async function signAuthorization< type: account.type, }) - const authorization = { - contractAddress, - chainId, - nonce, - } as Authorization - - if (typeof authorization.chainId === 'undefined') - authorization.chainId = - client.chain?.id ?? - (await getAction(client, getChainId, 'getChainId')({})) - - if (typeof authorization.nonce === 'undefined') { - authorization.nonce = await getAction( - client, - getTransactionCount, - 'getTransactionCount', - )({ - address: account.address, - blockTag: 'pending', - }) - if ( - !delegate || - (delegate !== true && isAddressEqual(account.address, delegate.address)) - ) - authorization.nonce += 1 - } - + const authorization = await prepareAuthorization(client, parameters) return account.experimental_signAuthorization(authorization) } diff --git a/src/experimental/eip7702/decorators/eip7702.test.ts b/src/experimental/eip7702/decorators/eip7702.test.ts index 503b3c00c2..3200a2a3f6 100644 --- a/src/experimental/eip7702/decorators/eip7702.test.ts +++ b/src/experimental/eip7702/decorators/eip7702.test.ts @@ -12,6 +12,7 @@ const client = anvilMainnet test('default', async () => { expect(eip7702Actions()(client)).toMatchInlineSnapshot(` { + "prepareAuthorization": [Function], "signAuthorization": [Function], } `) diff --git a/src/experimental/eip7702/decorators/eip7702.ts b/src/experimental/eip7702/decorators/eip7702.ts index 6f1b0eefd4..88cc1bf347 100644 --- a/src/experimental/eip7702/decorators/eip7702.ts +++ b/src/experimental/eip7702/decorators/eip7702.ts @@ -2,6 +2,11 @@ import type { Client } from '../../../clients/createClient.js' import type { Transport } from '../../../clients/transports/createTransport.js' import type { Account } from '../../../types/account.js' import type { Chain } from '../../../types/chain.js' +import { + type PrepareAuthorizationParameters, + type PrepareAuthorizationReturnType, + prepareAuthorization, +} from '../actions/prepareAuthorization.js' import { type SignAuthorizationParameters, type SignAuthorizationReturnType, @@ -11,6 +16,52 @@ import { export type Eip7702Actions< account extends Account | undefined = Account | undefined, > = { + /** + * Prepares an [EIP-7702 Authorization](https://eips.ethereum.org/EIPS/eip-7702) object for signing. + * This Action will fill the required fields of the Authorization object if they are not provided (e.g. `nonce` and `chainId`). + * + * With the prepared Authorization object, you can use [`signAuthorization`](https://viem.sh/experimental/eip7702/signAuthorization) to sign over the Authorization object. + * + * @param client - Client to use + * @param parameters - {@link PrepareAuthorizationParameters} + * @returns The prepared Authorization object. {@link PrepareAuthorizationReturnType} + * + * @example + * import { createClient, http } from 'viem' + * import { privateKeyToAccount } from 'viem/accounts' + * import { mainnet } from 'viem/chains' + * import { eip7702Actions } from 'viem/experimental' + * + * const client = createClient({ + * chain: mainnet, + * transport: http(), + * }).extend(eip7702Actions()) + * + * const authorization = await client.prepareAuthorization({ + * account: privateKeyToAccount('0x..'), + * contractAddress: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + * }) + * + * @example + * // Account Hoisting + * import { createClient, http } from 'viem' + * import { privateKeyToAccount } from 'viem/accounts' + * import { mainnet } from 'viem/chains' + * import { eip7702Actions } from 'viem/experimental' + * + * const client = createClient({ + * account: privateKeyToAccount('0x…'), + * chain: mainnet, + * transport: http(), + * }).extend(eip7702Actions()) + * + * const authorization = await client.prepareAuthorization({ + * contractAddress: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + * }) + */ + prepareAuthorization: ( + parameters: PrepareAuthorizationParameters, + ) => Promise /** * Signs an [EIP-7702 Authorization](https://eips.ethereum.org/EIPS/eip-7702) object. * @@ -80,6 +131,8 @@ export function eip7702Actions() { client: Client, ): Eip7702Actions => { return { + prepareAuthorization: (parameters) => + prepareAuthorization(client, parameters), signAuthorization: (parameters) => signAuthorization(client, parameters), } } diff --git a/src/experimental/index.ts b/src/experimental/index.ts index 756aa8d45c..d8fe90474e 100644 --- a/src/experimental/index.ts +++ b/src/experimental/index.ts @@ -43,6 +43,12 @@ export { type Eip7702Actions, eip7702Actions, } from './eip7702/decorators/eip7702.js' +export { + type PrepareAuthorizationParameters, + type PrepareAuthorizationReturnType, + type PrepareAuthorizationErrorType, + prepareAuthorization, +} from './eip7702/actions/prepareAuthorization.js' export { type SignAuthorizationParameters, type SignAuthorizationReturnType,