diff --git a/aa-sdk/core/src/account/smartContractAccount.ts b/aa-sdk/core/src/account/smartContractAccount.ts index 1e9bed30c9..f789e66a33 100644 --- a/aa-sdk/core/src/account/smartContractAccount.ts +++ b/aa-sdk/core/src/account/smartContractAccount.ts @@ -14,6 +14,7 @@ import { type TypedDataDefinition, } from "viem"; import { toAccount } from "viem/accounts"; +import type { Authorization } from "viem/experimental"; import { createBundlerClient } from "../client/bundlerClient.js"; import type { EntryPointDef, @@ -125,6 +126,7 @@ export type SmartContractAccount< getFactoryData: () => Promise; getEntryPoint: () => EntryPointDef; getImplementationAddress: () => Promise; + signAuthorization?: () => Promise>; }; // [!endregion SmartContractAccount] @@ -155,7 +157,10 @@ export type ToSmartContractAccountParams< // if not provided, will default to just using signMessage over the Hex signUserOperationHash?: (uoHash: Hex) => Promise; encodeUpgradeToAndCall?: (params: UpgradeToAndCallParams) => Promise; -} & Omit; +} & Omit< + CustomSource, + "signTransaction" | "address" | "experimental_signAuthorization" +>; // [!endregion ToSmartContractAccountParams] /** diff --git a/aa-sdk/core/src/actions/smartAccount/internal/sendUserOperation.ts b/aa-sdk/core/src/actions/smartAccount/internal/sendUserOperation.ts index e89aeaecc5..558c3de980 100644 --- a/aa-sdk/core/src/actions/smartAccount/internal/sendUserOperation.ts +++ b/aa-sdk/core/src/actions/smartAccount/internal/sendUserOperation.ts @@ -50,6 +50,16 @@ export async function _sendUserOperation< overrides, }); + if ( + request.signature.startsWith( + // TODO: put this in a constant + "0x00000000000000000000000000000000000000000000000001ff00" + ) && + account.signAuthorization + ) { + request.authorizationTuple = await account.signAuthorization(); + } + return { hash: await client.sendRawUserOperation(request, entryPoint.address), request, diff --git a/aa-sdk/core/src/entrypoint/0.7.ts b/aa-sdk/core/src/entrypoint/0.7.ts index 678b4cb5a5..327c80e479 100644 --- a/aa-sdk/core/src/entrypoint/0.7.ts +++ b/aa-sdk/core/src/entrypoint/0.7.ts @@ -65,7 +65,7 @@ const packUserOperation = (request: UserOperationRequest<"0.7.0">): Hex => { { type: "bytes32" }, ], [ - request.sender as Address, + request.sender, hexToBigInt(request.nonce), keccak256(initCode), keccak256(request.callData), diff --git a/aa-sdk/core/src/index.ts b/aa-sdk/core/src/index.ts index 3a30e59763..af5ba54b2e 100644 --- a/aa-sdk/core/src/index.ts +++ b/aa-sdk/core/src/index.ts @@ -89,6 +89,7 @@ export { } from "./errors/useroperation.js"; export { LogLevel, Logger } from "./logger.js"; export { middlewareActions } from "./middleware/actions.js"; +export { default7702UserOpSigner } from "./middleware/defaults/7702signer.js"; export { defaultFeeEstimator } from "./middleware/defaults/feeEstimator.js"; export { defaultGasEstimator } from "./middleware/defaults/gasEstimator.js"; export { defaultPaymasterAndData } from "./middleware/defaults/paymasterAndData.js"; diff --git a/aa-sdk/core/src/middleware/defaults/7702signer.ts b/aa-sdk/core/src/middleware/defaults/7702signer.ts new file mode 100644 index 0000000000..03653749cc --- /dev/null +++ b/aa-sdk/core/src/middleware/defaults/7702signer.ts @@ -0,0 +1,49 @@ +import { AccountNotFoundError } from "../../errors/account.js"; +import type { ClientMiddlewareFn } from "../types"; +import { defaultUserOpSigner } from "./userOpSigner.js"; + +/** + * Provides a default middleware function for signing user operations with a client account when using ERC-7702 to upgrade local accounts to smart accounts. + * If the SmartAccount doesn't support `signAuthorization`, then this just runs the default UserOpSigner middleware + * + * @param {UserOperationStruct} struct The user operation structure to be signed + * @param {*} params The middleware context containing the client and account information + * @param {Client} params.client The client object, which should include account and chain information + * @param {Account} [params.account] Optional, the account used for signing, defaults to the client's account if not provided + * @returns {Promise} A promise that resolves to the signed user operation structure + */ +export const default7702UserOpSigner: ClientMiddlewareFn = async ( + struct, + params +) => { + const uo = await defaultUserOpSigner(struct, params); + const account = params.account ?? params.client.account; + if (!account) { + throw new AccountNotFoundError(); + } + + if (!account.signAuthorization) { + return uo; + } + + const code = + (await params.client.getCode({ address: account.address })) ?? "0x"; + // TODO: this isn't the cleanest because now the account implementation HAS to know that it needs to return an impl address + // even if the account is not deployed + const implAddress = await account.getImplementationAddress(); + if ( + code === + "0x00000000000000000000000000000000000000000000000001ff00" + + implAddress.slice(2) + ) { + return uo; + } + + return { + ...uo, + // TODO: put this in a constant + signature: + "0x00000000000000000000000000000000000000000000000001ff00" + + (await uo.signature).slice(2), + }; +}; diff --git a/aa-sdk/core/src/types.ts b/aa-sdk/core/src/types.ts index bbe276f2e4..9bc9fa2b22 100644 --- a/aa-sdk/core/src/types.ts +++ b/aa-sdk/core/src/types.ts @@ -5,6 +5,7 @@ import { type StateOverride, type TransactionReceipt, } from "viem"; +import type { Authorization } from "viem/experimental"; import type { z } from "zod"; import type { UserOperationFeeOptionsFieldSchema, @@ -205,11 +206,11 @@ export interface UserOperationRequest_v7 { // Reference: https://eips.ethereum.org/EIPS/eip-4337#definitions export type UserOperationRequest< TEntryPointVersion extends EntryPointVersion = EntryPointVersion -> = TEntryPointVersion extends "0.6.0" +> = (TEntryPointVersion extends "0.6.0" ? UserOperationRequest_v6 : TEntryPointVersion extends "0.7.0" ? UserOperationRequest_v7 - : never; + : never) & { authorizationTuple?: Authorization }; // [!endregion UserOperationRequest] diff --git a/aa-sdk/core/src/version.ts b/aa-sdk/core/src/version.ts index 20a5944aef..9bd0555a10 100644 --- a/aa-sdk/core/src/version.ts +++ b/aa-sdk/core/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.5.1"; +export const VERSION = "4.6.0"; diff --git a/account-kit/core/src/version.ts b/account-kit/core/src/version.ts index 20a5944aef..9bd0555a10 100644 --- a/account-kit/core/src/version.ts +++ b/account-kit/core/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.5.1"; +export const VERSION = "4.6.0"; diff --git a/account-kit/infra/src/version.ts b/account-kit/infra/src/version.ts index 20a5944aef..9bd0555a10 100644 --- a/account-kit/infra/src/version.ts +++ b/account-kit/infra/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.5.1"; +export const VERSION = "4.6.0"; diff --git a/account-kit/plugingen/src/version.ts b/account-kit/plugingen/src/version.ts index 20a5944aef..9bd0555a10 100644 --- a/account-kit/plugingen/src/version.ts +++ b/account-kit/plugingen/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.5.1"; +export const VERSION = "4.6.0"; diff --git a/account-kit/react/src/version.ts b/account-kit/react/src/version.ts index 20a5944aef..9bd0555a10 100644 --- a/account-kit/react/src/version.ts +++ b/account-kit/react/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.5.1"; +export const VERSION = "4.6.0"; diff --git a/account-kit/signer/src/version.ts b/account-kit/signer/src/version.ts index 20a5944aef..9bd0555a10 100644 --- a/account-kit/signer/src/version.ts +++ b/account-kit/signer/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.5.1"; +export const VERSION = "4.6.0"; diff --git a/site/pages/reference/aa-sdk/core/functions/default7702UserOpSigner.mdx b/site/pages/reference/aa-sdk/core/functions/default7702UserOpSigner.mdx new file mode 100644 index 0000000000..f5fcc4ac66 --- /dev/null +++ b/site/pages/reference/aa-sdk/core/functions/default7702UserOpSigner.mdx @@ -0,0 +1,43 @@ +--- +# This file is autogenerated +title: default7702UserOpSigner +description: Overview of the default7702UserOpSigner method +--- + +# default7702UserOpSigner + +Provides a default middleware function for signing user operations with a client account when using ERC-7702 to upgrade local accounts to smart accounts. +If the SmartAccount doesn't support `signAuthorization`, then this just runs the default UserOpSigner middleware + +## Import + +```ts +import { default7702UserOpSigner } from "@aa-sdk/core"; +``` + +## Parameters + +### struct + +`UserOperationStruct` +The user operation structure to be signed + +### params + +`*` +The middleware context containing the client and account information + +### params.client + +`Client` +The client object, which should include account and chain information + +### params.account + +`Account` +Optional, the account used for signing, defaults to the client's account if not provided + +## Returns + +`Promise` +A promise that resolves to the signed user operation structure