3rd Party Smart Contracts

You are not limited to the accounts defined in @account-kit/smart-contracts. If you’d like to bring your own smart account, you have a couple options:

  1. Use a 3rd party library that exports an aa-sdk compatible SmartContractAccount interface.
  2. Implement your own SmartContractAccount interface using toSmartContractAccount.

Third Party SDKs

If you’ve built an SDK or Guide that leverages any of the methods above to use as a Smart Contract within Account Kit, we’re happy to include you in this list!

Please open a PR to add a link to your content in this section.

Using toSmartContractAccount

The SmartAccountClient can be used with any smart account because it only relies on the SmartContractAccount interface. This means you can use your own smart account implementation with Account Kit.

import { 
function getEntryPoint<TEntryPointVersion extends EntryPointVersion = "0.6.0", TChain extends Chain = Chain>(chain: TChain, options: GetEntryPointOptions<TEntryPointVersion>): EntryPointDefRegistry<TChain>[TEntryPointVersion] (+2 overloads)
getEntryPoint
,
function toSmartContractAccount<Name extends string = string, TTransport extends Transport = Transport, TChain extends Chain = Chain, TEntryPointVersion extends EntryPointVersion = keyof EntryPointRegistryBase<unknown>>({ transport, chain, entryPoint, source, accountAddress, getAccountInitCode, getNonce, signMessage, signTypedData, encodeBatchExecute, encodeExecute, getDummySignature, signUserOperationHash, encodeUpgradeToAndCall, }: ToSmartContractAccountParams<Name, TTransport, TChain, TEntryPointVersion>): Promise<SmartContractAccount<Name, TEntryPointVersion>>
toSmartContractAccount
} from "@aa-sdk/core";
import {
function http<rpcSchema extends RpcSchema | undefined = undefined, raw extends boolean = false>(url?: string | undefined, config?: HttpTransportConfig<rpcSchema, raw>): HttpTransport<rpcSchema, raw>
http
, type
type SignableMessage = string | { raw: Hex | ByteArray; }
SignableMessage
, type
type Hash = `0x${string}`
Hash
} from "viem";
import {
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
sepolia
} from "viem/chains";
const
const myAccount: SmartContractAccount<"MyAccount", "0.6.0">
myAccount
= await
toSmartContractAccount<"MyAccount", HttpTransport<undefined, false>, { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }, "0.6.0">({ transport, chain, entryPoint, source, accountAddress, getAccountInitCode, getNonce, signMessage, signTypedData, encodeBatchExecute, encodeExecute, getDummySignature, signUserOperationHash, encodeUpgradeToAndCall, }: ToSmartContractAccountParams<...>): Promise<...>
toSmartContractAccount
({
/// REQUIRED PARAMS ///
source: "MyAccount"
source
: "MyAccount",
transport: HttpTransport<undefined, false>
transport
:
http<undefined, false>(url?: string | undefined, config?: HttpTransportConfig<undefined, false> | undefined): HttpTransport<undefined, false>
http
("RPC_URL"),
chain: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
chain
:
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
sepolia
,
// The EntryPointDef that your account is compatible with
entryPoint: EntryPointDef<"0.6.0", { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }>
entryPoint
:
getEntryPoint<"0.6.0", { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }>(chain: { ...; }, options?: { ...; } | undefined): EntryPointDef<...> (+2 overloads)
getEntryPoint
(
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
sepolia
),
// This should return a concatenation of your `factoryAddress` and the `callData` for your factory's create account method
getAccountInitCode: () => Promise<Hex>
getAccountInitCode
: async ():
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x{factoryAddress}{callData}",
// an invalid signature that doesn't cause your account to revert during validation
getDummySignature: () => Hex | Promise<Hex>
getDummySignature
: async ():
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x1234...",
// given a UO in the form of {target, data, value} should output the calldata for calling your contract's execution method
encodeExecute: (tx: AccountOp) => Promise<Hex>
encodeExecute
: async (
uo: AccountOp
uo
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x....",
signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>
signMessage
: async ({
message: SignableMessage
message
}):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x...",
signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>
signTypedData
: async (
typedData: TypedDataDefinition<typedData, primaryType, typedData extends { [x: string]: readonly TypedDataParameter[]; [x: `string[${string}]`]: undefined; [x: `function[${string}]`]: undefined; [x: `address[${string}]`]: undefined; [x: `bool[${string}]`]: undefined; [x: `bytes[${string}]`]: undefined; [x: `bytes1[${string}]`]: undefined; [x: `bytes2[${string}]`]: undefined; [x: `bytes3[${string}]`]: undefined; [x: `bytes4[${string}]`]: undefined; [x: `bytes5[${string}]`]: undefined; [x: `bytes6[${string}]`]: undefined; [x: `bytes7[${string}]`]: undefined; [x: `bytes8[${string}]`]: undefined; [x: `bytes9[${string}]`]: undefined; [x: `bytes10[${string}]`]: undefined; [x: `bytes11[${string}]`]: undefined; [x: `bytes12[${string}]`]: undefined; [x: `bytes13[${string}]`]: undefined; [x: `bytes14[${string}]`]: undefined; [x: `bytes15[${string}]`]: undefined; [x: `bytes16[${string}]`]: undefined; [x: `bytes17[${string}]`]: undefined; [x: `bytes18[${string}]`]: undefined; [x: `bytes19[${string}]`]: undefined; [x: `bytes20[${string}]`]: undefined; [x: `bytes21[${string}]`]: undefined; [x: `bytes22[${string}]`]: undefined; [x: `bytes23[${string}]`]: undefined; [x: `bytes24[${string}]`]: undefined; [x: `bytes25[${string}]`]: undefined; [x: `bytes26[${string}]`]: undefined; [x: `bytes27[${string}]`]: undefined; [x: `bytes28[${string}]`]: undefined; [x: `bytes29[${string}]`]: undefined; [x: `bytes30[${string}]`]: undefined; [x: `bytes31[${string}]`]: undefined ...
typedData
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x000",
/// OPTIONAL PARAMS /// // if you already know your account's address, pass that in here to avoid generating a new counterfactual
accountAddress?: `0x${string}` | undefined
accountAddress
: "0x...",
// if your account supports batching, this should take an array of UOs and return the calldata for calling your contract's batchExecute method
encodeBatchExecute?: ((txs: AccountOp[]) => Promise<Hex>) | undefined
encodeBatchExecute
: async (
uos: AccountOp[]
uos
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x...",
// if your contract expects a different signing scheme than the default signMessage scheme, you can override that here
signUserOperationHash?: ((uoHash: Hex) => Promise<Hex>) | undefined
signUserOperationHash
: async (
hash: `0x${string}`
hash
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x...",
// allows you to define the calldata for upgrading your account
encodeUpgradeToAndCall?: ((params: UpgradeToAndCallParams) => Promise<Hex>) | undefined
encodeUpgradeToAndCall
: async (
params: UpgradeToAndCallParams
params
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x...",
});

To use your account, you will need to pass it into a SmartAccountClient.

import { 
function createAlchemySmartAccountClient<TChain extends Chain = Chain, TAccount extends SmartContractAccount | undefined = SmartContractAccount | undefined, TContext extends UserOperationContext | undefined = UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<TChain, TAccount, TContext>): AlchemySmartAccountClient<TChain, TAccount, Record<string, never>, TContext>
createAlchemySmartAccountClient
,
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
} from "@account-kit/infra";
import {
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
sepolia
} from "viem/chains";
import {
import myAccount
myAccount
} from "./my-account";
const
const client: { [x: string]: never; account: any; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; ... 84 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
client
=
createAlchemySmartAccountClient<{ blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }, any, UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<...>): { ...; }
createAlchemySmartAccountClient
({
// created above
account?: any
account
:
import myAccount
myAccount
,
chain?: Chain | { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; } | undefined

Chain for the client.

chain
:
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
sepolia
,
transport: AlchemyTransport

The RPC transport

transport
:
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
: "YOUR_API_KEY",
}), });
import { 
function getEntryPoint<TEntryPointVersion extends EntryPointVersion = "0.6.0", TChain extends Chain = Chain>(chain: TChain, options: GetEntryPointOptions<TEntryPointVersion>): EntryPointDefRegistry<TChain>[TEntryPointVersion] (+2 overloads)
getEntryPoint
,
function toSmartContractAccount<Name extends string = string, TTransport extends Transport = Transport, TChain extends Chain = Chain, TEntryPointVersion extends EntryPointVersion = keyof EntryPointRegistryBase<unknown>>({ transport, chain, entryPoint, source, accountAddress, getAccountInitCode, getNonce, signMessage, signTypedData, encodeBatchExecute, encodeExecute, getDummySignature, signUserOperationHash, encodeUpgradeToAndCall, }: ToSmartContractAccountParams<Name, TTransport, TChain, TEntryPointVersion>): Promise<SmartContractAccount<Name, TEntryPointVersion>>
toSmartContractAccount
} from "@aa-sdk/core";
import {
function http<rpcSchema extends RpcSchema | undefined = undefined, raw extends boolean = false>(url?: string | undefined, config?: HttpTransportConfig<rpcSchema, raw>): HttpTransport<rpcSchema, raw>
http
, type
type SignableMessage = string | { raw: Hex | ByteArray; }
SignableMessage
, type
type Hash = `0x${string}`
Hash
} from "viem";
import {
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
sepolia
} from "viem/chains";
export const
const myAccount: SmartContractAccount<"MyAccount", "0.6.0">
myAccount
= await
toSmartContractAccount<"MyAccount", HttpTransport<undefined, false>, { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }, "0.6.0">({ transport, chain, entryPoint, source, accountAddress, getAccountInitCode, getNonce, signMessage, signTypedData, encodeBatchExecute, encodeExecute, getDummySignature, signUserOperationHash, encodeUpgradeToAndCall, }: ToSmartContractAccountParams<...>): Promise<...>
toSmartContractAccount
({
/// REQUIRED PARAMS ///
source: "MyAccount"
source
: "MyAccount",
transport: HttpTransport<undefined, false>
transport
:
http<undefined, false>(url?: string | undefined, config?: HttpTransportConfig<undefined, false> | undefined): HttpTransport<undefined, false>
http
("RPC_URL"),
chain: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
chain
:
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
sepolia
,
// The EntryPointDef that your account is compatible with
entryPoint: EntryPointDef<"0.6.0", { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }>
entryPoint
:
getEntryPoint<"0.6.0", { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }>(chain: { ...; }, options?: { ...; } | undefined): EntryPointDef<...> (+2 overloads)
getEntryPoint
(
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
sepolia
),
// This should return a concatenation of your `factoryAddress` and the `callData` for your factory's create account method
getAccountInitCode: () => Promise<Hex>
getAccountInitCode
: async ():
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x{factoryAddress}{callData}",
// an invalid signature that doesn't cause your account to revert during validation
getDummySignature: () => Hex | Promise<Hex>
getDummySignature
: async ():
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x1234...",
// given a UO in the form of {target, data, value} should output the calldata for calling your contract's execution method
encodeExecute: (tx: AccountOp) => Promise<Hex>
encodeExecute
: async (
uo: AccountOp
uo
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x....",
signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>
signMessage
: async ({
message: SignableMessage
message
}):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x...",
signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>
signTypedData
: async (
typedData: TypedDataDefinition<typedData, primaryType, typedData extends { [x: string]: readonly TypedDataParameter[]; [x: `string[${string}]`]: undefined; [x: `function[${string}]`]: undefined; [x: `address[${string}]`]: undefined; [x: `bool[${string}]`]: undefined; [x: `bytes[${string}]`]: undefined; [x: `bytes1[${string}]`]: undefined; [x: `bytes2[${string}]`]: undefined; [x: `bytes3[${string}]`]: undefined; [x: `bytes4[${string}]`]: undefined; [x: `bytes5[${string}]`]: undefined; [x: `bytes6[${string}]`]: undefined; [x: `bytes7[${string}]`]: undefined; [x: `bytes8[${string}]`]: undefined; [x: `bytes9[${string}]`]: undefined; [x: `bytes10[${string}]`]: undefined; [x: `bytes11[${string}]`]: undefined; [x: `bytes12[${string}]`]: undefined; [x: `bytes13[${string}]`]: undefined; [x: `bytes14[${string}]`]: undefined; [x: `bytes15[${string}]`]: undefined; [x: `bytes16[${string}]`]: undefined; [x: `bytes17[${string}]`]: undefined; [x: `bytes18[${string}]`]: undefined; [x: `bytes19[${string}]`]: undefined; [x: `bytes20[${string}]`]: undefined; [x: `bytes21[${string}]`]: undefined; [x: `bytes22[${string}]`]: undefined; [x: `bytes23[${string}]`]: undefined; [x: `bytes24[${string}]`]: undefined; [x: `bytes25[${string}]`]: undefined; [x: `bytes26[${string}]`]: undefined; [x: `bytes27[${string}]`]: undefined; [x: `bytes28[${string}]`]: undefined; [x: `bytes29[${string}]`]: undefined; [x: `bytes30[${string}]`]: undefined; [x: `bytes31[${string}]`]: undefined ...
typedData
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x000",
/// OPTIONAL PARAMS /// // if you already know your account's address, pass that in here to avoid generating a new counterfactual
accountAddress?: `0x${string}` | undefined
accountAddress
: "0x...",
// if your account supports batching, this should take an array of UOs and return the calldata for calling your contract's batchExecute method
encodeBatchExecute?: ((txs: AccountOp[]) => Promise<Hex>) | undefined
encodeBatchExecute
: async (
uos: AccountOp[]
uos
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x...",
// if your contract expects a different signing scheme than the default signMessage scheme, you can override that here
signUserOperationHash?: ((uoHash: Hex) => Promise<Hex>) | undefined
signUserOperationHash
: async (
hash: `0x${string}`
hash
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x...",
// allows you to define the calldata for upgrading your account
encodeUpgradeToAndCall?: ((params: UpgradeToAndCallParams) => Promise<Hex>) | undefined
encodeUpgradeToAndCall
: async (
params: UpgradeToAndCallParams
params
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Hash = `0x${string}`
Hash
> => "0x...",
});

LightSmartContractAccount as an Example

We have built an extension of the eth-infinitism SimpleAccount called LightAccount.sol. You can learn more about Light Account in the Light Account documentation.

We provide an implementation of SmartContractAccount that works with LightAccount.sol, which can be used as an example of how to implement your own Smart Contract Account:

1import {
2 createBundlerClient,
3 getEntryPoint,
4 type Address,
5 type EntryPointDef,
6 type SmartAccountSigner,
7} from "@aa-sdk/core";
8import {
9 concatHex,
10 encodeFunctionData,
11 type Chain,
12 type Hex,
13 type Transport,
14} from "viem";
15import { LightAccountAbi_v1 } from "../abis/LightAccountAbi_v1.js";
16import { LightAccountAbi_v2 } from "../abis/LightAccountAbi_v2.js";
17import { LightAccountFactoryAbi_v1 } from "../abis/LightAccountFactoryAbi_v1.js";
18import { LightAccountFactoryAbi_v2 } from "../abis/LightAccountFactoryAbi_v2.js";
19import type {
20 LightAccountEntryPointVersion,
21 LightAccountVersion,
22} from "../types.js";
23import {
24 AccountVersionRegistry,
25 LightAccountUnsupported1271Factories,
26 defaultLightAccountVersion,
27 getDefaultLightAccountFactoryAddress,
28} from "../utils.js";
29import {
30 createLightAccountBase,
31 type CreateLightAccountBaseParams,
32 type LightAccountBase,
33} from "./base.js";
34import { predictLightAccountAddress } from "./predictAddress.js";
35
36export type LightAccount<
37 TSigner extends SmartAccountSigner = SmartAccountSigner,
38 TLightAccountVersion extends
39 LightAccountVersion<"LightAccount"> = LightAccountVersion<"LightAccount">,
40> = LightAccountBase<TSigner, "LightAccount", TLightAccountVersion> & {
41 encodeTransferOwnership: (newOwner: Address) => Hex;
42 getOwnerAddress: () => Promise<Address>;
43};
44
45// [!region CreateLightAccountParams]
46export type CreateLightAccountParams<
47 TTransport extends Transport = Transport,
48 TSigner extends SmartAccountSigner = SmartAccountSigner,
49 TLightAccountVersion extends
50 LightAccountVersion<"LightAccount"> = LightAccountVersion<"LightAccount">,
51> = Omit<
52 CreateLightAccountBaseParams<
53 "LightAccount",
54 TLightAccountVersion,
55 TTransport,
56 TSigner
57 >,
58 | "getAccountInitCode"
59 | "entryPoint"
60 | "version"
61 | "abi"
62 | "accountAddress"
63 | "type"
64> & {
65 salt?: bigint;
66 initCode?: Hex;
67 accountAddress?: Address;
68 factoryAddress?: Address;
69 version?: TLightAccountVersion;
70 entryPoint?: EntryPointDef<
71 LightAccountEntryPointVersion<"LightAccount", TLightAccountVersion>,
72 Chain
73 >;
74};
75// [!endregion CreateLightAccountParams]
76
77export async function createLightAccount<
78 TTransport extends Transport = Transport,
79 TSigner extends SmartAccountSigner = SmartAccountSigner,
80 TLightAccountVersion extends LightAccountVersion<"LightAccount"> = "v2.0.0",
81>(
82 config: CreateLightAccountParams<TTransport, TSigner, TLightAccountVersion>,
83): Promise<LightAccount<TSigner, TLightAccountVersion>>;
84
85/**
86 * Creates a light account based on the provided parameters such as transport, chain, signer, init code, and more. Ensures that an account is configured and returned with various capabilities, such as transferring ownership and retrieving the owner's address.
87 *
88 * @example
89 * ```ts
90 * import { createLightAccount } from "@account-kit/smart-contracts";
91 * import { LocalAccountSigner } from "@aa-sdk/core";
92 * import { sepolia } from "viem/chains";
93 * import { http, generatePrivateKey } from "viem"
94 *
95 * const account = await createLightAccount({
96 * chain: sepolia,
97 * transport: http("RPC_URL"),
98 * signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey())
99 * });
100 * ```
101 *
102 * @param {CreateLightAccountParams} config The parameters for creating a light account
103 * @returns {Promise<LightAccount>} A promise that resolves to a `LightAccount` object containing the created account information and methods
104 */
105export async function createLightAccount({
106 transport,
107 chain,
108 signer,
109 initCode,
110 version = defaultLightAccountVersion(),
111 entryPoint = getEntryPoint(chain, {
112 version: AccountVersionRegistry["LightAccount"][version]
113 .entryPointVersion as any,
114 }),
115 accountAddress,
116 factoryAddress = getDefaultLightAccountFactoryAddress(chain, version),
117 salt: salt_ = 0n,
118}: CreateLightAccountParams): Promise<LightAccount> {
119 const client = createBundlerClient({
120 transport,
121 chain,
122 });
123
124 const accountAbi =
125 version === "v2.0.0" ? LightAccountAbi_v2 : LightAccountAbi_v1;
126 const factoryAbi =
127 version === "v2.0.0"
128 ? LightAccountFactoryAbi_v1
129 : LightAccountFactoryAbi_v2;
130
131 const signerAddress = await signer.getAddress();
132
133 const salt = LightAccountUnsupported1271Factories.has(
134 factoryAddress.toLowerCase() as Address,
135 )
136 ? 0n
137 : salt_;
138
139 const getAccountInitCode = async () => {
140 if (initCode) return initCode;
141
142 return concatHex([
143 factoryAddress,
144 encodeFunctionData({
145 abi: factoryAbi,
146 functionName: "createAccount",
147 args: [signerAddress, salt],
148 }),
149 ]);
150 };
151
152 const address =
153 accountAddress ??
154 predictLightAccountAddress({
155 factoryAddress,
156 salt,
157 ownerAddress: signerAddress,
158 version,
159 });
160
161 const account = await createLightAccountBase<
162 "LightAccount",
163 LightAccountVersion<"LightAccount">,
164 Transport,
165 SmartAccountSigner
166 >({
167 transport,
168 chain,
169 signer,
170 abi: accountAbi,
171 type: "LightAccount",
172 version,
173 entryPoint,
174 accountAddress: address,
175 getAccountInitCode,
176 });
177
178 return {
179 ...account,
180
181 encodeTransferOwnership: (newOwner: Address) => {
182 return encodeFunctionData({
183 abi: accountAbi,
184 functionName: "transferOwnership",
185 args: [newOwner],
186 });
187 },
188 async getOwnerAddress(): Promise<Address> {
189 const callResult = await client.readContract({
190 address,
191 abi: accountAbi,
192 functionName: "owner",
193 });
194
195 if (callResult == null) {
196 throw new Error("could not get on-chain owner");
197 }
198
199 return callResult;
200 },
201 };
202}

The toSmartContractAccount Method

For your reference, this is the definition of the toSmartContractAccount interface as pulled from the source code:

1import {
2 getContract,
3 hexToBytes,
4 type Address,
5 type Chain,
6 type CustomSource,
7 type Hex,
8 type LocalAccount,
9 type PublicClient,
10 type SignableMessage,
11 type Transport,
12 type TypedData,
13 type TypedDataDefinition,
14} from "viem";
15import { toAccount } from "viem/accounts";
16import { createBundlerClient } from "../client/bundlerClient.js";
17import type {
18 EntryPointDef,
19 EntryPointRegistryBase,
20 EntryPointVersion,
21} from "../entrypoint/types.js";
22import {
23 BatchExecutionNotSupportedError,
24 FailedToGetStorageSlotError,
25 GetCounterFactualAddressError,
26 SignTransactionNotSupportedError,
27 UpgradesNotSupportedError,
28} from "../errors/account.js";
29import { InvalidRpcUrlError } from "../errors/client.js";
30import { InvalidEntryPointError } from "../errors/entrypoint.js";
31import { Logger } from "../logger.js";
32import type { SmartAccountSigner } from "../signer/types.js";
33import { wrapSignatureWith6492 } from "../signer/utils.js";
34import type { NullAddress } from "../types.js";
35import type { IsUndefined, Never } from "../utils/types.js";
36
37export type AccountOp = {
38 target: Address;
39 value?: bigint;
40 data: Hex | "0x";
41};
42
43export enum DeploymentState {
44 UNDEFINED = "0x0",
45 NOT_DEPLOYED = "0x1",
46 DEPLOYED = "0x2",
47}
48
49export type SignatureRequest =
50 | {
51 type: "personal_sign";
52 data: SignableMessage;
53 }
54 | {
55 type: "eth_signTypedData_v4";
56 data: TypedDataDefinition;
57 };
58
59export type SigningMethods = {
60 prepareSign: (request: SignatureRequest) => Promise<SignatureRequest>;
61 formatSign: (signature: Hex) => Promise<Hex>;
62};
63
64export type GetEntryPointFromAccount<
65 TAccount extends SmartContractAccount | undefined,
66 TAccountOverride extends SmartContractAccount = SmartContractAccount,
67> =
68 GetAccountParameter<TAccount, TAccountOverride> extends SmartContractAccount<
69 string,
70 infer TEntryPointVersion
71 >
72 ? TEntryPointVersion
73 : EntryPointVersion;
74
75export type GetAccountParameter<
76 TAccount extends SmartContractAccount | undefined =
77 | SmartContractAccount
78 | undefined,
79 TAccountOverride extends SmartContractAccount = SmartContractAccount,
80> =
81 IsUndefined<TAccount> extends true
82 ? { account: TAccountOverride }
83 : { account?: TAccountOverride };
84
85export type UpgradeToAndCallParams = {
86 upgradeToAddress: Address;
87 upgradeToInitData: Hex;
88};
89
90export type SmartContractAccountWithSigner<
91 Name extends string = string,
92 TSigner extends SmartAccountSigner = SmartAccountSigner,
93 TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
94> = SmartContractAccount<Name, TEntryPointVersion> & {
95 getSigner: () => TSigner;
96};
97
98/**
99 * Determines if the given SmartContractAccount has a signer associated with it.
100 *
101 * @example
102 * ```ts
103 * import { toSmartContractAccount } from "@aa-sdk/core";
104 *
105 * const account = await toSmartContractAccount(...);
106 *
107 * console.log(isSmartAccountWithSigner(account)); // false: the base account does not have a publicly accessible signer
108 * ```
109 *
110 * @param {SmartContractAccount} account The account to check.
111 * @returns {boolean} true if the account has a signer, otherwise false.
112 */
113export const isSmartAccountWithSigner = (
114 account: SmartContractAccount,
115): account is SmartContractAccountWithSigner => {
116 return "getSigner" in account;
117};
118
119// [!region SmartContractAccount]
120export type SmartContractAccount<
121 Name extends string = string,
122 TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
123> = LocalAccount<Name> & {
124 source: Name;
125 getDummySignature: () => Hex | Promise<Hex>;
126 encodeExecute: (tx: AccountOp) => Promise<Hex>;
127 encodeBatchExecute: (txs: AccountOp[]) => Promise<Hex>;
128 signUserOperationHash: (uoHash: Hex) => Promise<Hex>;
129 signMessageWith6492: (params: { message: SignableMessage }) => Promise<Hex>;
130 signTypedDataWith6492: <
131 const typedData extends TypedData | Record<string, unknown>,
132 primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
133 >(
134 typedDataDefinition: TypedDataDefinition<typedData, primaryType>,
135 ) => Promise<Hex>;
136 encodeUpgradeToAndCall: (params: UpgradeToAndCallParams) => Promise<Hex>;
137 getAccountNonce(nonceKey?: bigint): Promise<bigint>;
138 getInitCode: () => Promise<Hex>;
139 isAccountDeployed: () => Promise<boolean>;
140 getFactoryAddress: () => Promise<Address>;
141 getFactoryData: () => Promise<Hex>;
142 getEntryPoint: () => EntryPointDef<TEntryPointVersion>;
143 getImplementationAddress: () => Promise<NullAddress | Address>;
144} & SigningMethods;
145// [!endregion SmartContractAccount]
146
147export interface AccountEntryPointRegistry<Name extends string = string>
148 extends EntryPointRegistryBase<
149 SmartContractAccount<Name, EntryPointVersion>
150 > {
151 "0.6.0": SmartContractAccount<Name, "0.6.0">;
152 "0.7.0": SmartContractAccount<Name, "0.7.0">;
153}
154
155// [!region ToSmartContractAccountParams]
156export type ToSmartContractAccountParams<
157 Name extends string = string,
158 TTransport extends Transport = Transport,
159 TChain extends Chain = Chain,
160 TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
161> = {
162 source: Name;
163 transport: TTransport;
164 chain: TChain;
165 entryPoint: EntryPointDef<TEntryPointVersion, TChain>;
166 accountAddress?: Address;
167 getAccountInitCode: () => Promise<Hex>;
168 getDummySignature: () => Hex | Promise<Hex>;
169 encodeExecute: (tx: AccountOp) => Promise<Hex>;
170 encodeBatchExecute?: (txs: AccountOp[]) => Promise<Hex>;
171 getNonce?: (nonceKey?: bigint) => Promise<bigint>;
172 // if not provided, will default to just using signMessage over the Hex
173 signUserOperationHash?: (uoHash: Hex) => Promise<Hex>;
174 encodeUpgradeToAndCall?: (params: UpgradeToAndCallParams) => Promise<Hex>;
175 getImplementationAddress?: () => Promise<NullAddress | Address>;
176} & Omit<CustomSource, "signTransaction" | "address"> &
177 (SigningMethods | Never<SigningMethods>);
178// [!endregion ToSmartContractAccountParams]
179
180/**
181 * Parses the factory address and factory calldata from the provided account initialization code (initCode).
182 *
183 * @example
184 * ```ts
185 * import { parseFactoryAddressFromAccountInitCode } from "@aa-sdk/core";
186 *
187 * const [address, calldata] = parseFactoryAddressFromAccountInitCode("0xAddressCalldata");
188 * ```
189 *
190 * @param {Hex} initCode The initialization code from which to parse the factory address and calldata
191 * @returns {[Address, Hex]} A tuple containing the parsed factory address and factory calldata
192 */
193export const parseFactoryAddressFromAccountInitCode = (
194 initCode: Hex,
195): [Address, Hex] => {
196 const factoryAddress: Address = `0x${initCode.substring(2, 42)}`;
197 const factoryCalldata: Hex = `0x${initCode.substring(42)}`;
198 return [factoryAddress, factoryCalldata];
199};
200
201export type GetAccountAddressParams = {
202 client: PublicClient;
203 entryPoint: EntryPointDef;
204 accountAddress?: Address;
205 getAccountInitCode: () => Promise<Hex>;
206};
207
208/**
209 * Retrieves the account address. Uses a provided `accountAddress` if available; otherwise, it computes the address using the entry point contract and the initial code.
210 *
211 * @example
212 * ```ts
213 * import { getEntryPoint, getAccountAddress } from "@aa-sdk/core";
214 *
215 * const accountAddress = await getAccountAddress({
216 * client,
217 * entryPoint: getEntryPoint(chain),
218 * getAccountInitCode: async () => "0x{factoryAddress}{factoryCallData}",
219 * });
220 * ```
221 *
222 * @param {GetAccountAddressParams} params The configuration object
223 * @param {PublicClient} params.client A public client instance to interact with the blockchain
224 * @param {EntryPointDef} params.entryPoint The entry point definition which includes the address and ABI
225 * @param {Address} params.accountAddress Optional existing account address
226 * @param {() => Promise<Hex>} params.getAccountInitCode A function that returns a Promise resolving to a Hex string representing the initial code of the account
227 * @returns {Promise<Address>} A promise that resolves to the account address
228 */
229export const getAccountAddress = async ({
230 client,
231 entryPoint,
232 accountAddress,
233 getAccountInitCode,
234}: GetAccountAddressParams) => {
235 if (accountAddress) return accountAddress;
236
237 const entryPointContract = getContract({
238 address: entryPoint.address,
239 abi: entryPoint.abi,
240 client,
241 });
242
243 const initCode = await getAccountInitCode();
244 Logger.verbose("[BaseSmartContractAccount](getAddress) initCode: ", initCode);
245
246 try {
247 await entryPointContract.simulate.getSenderAddress([initCode]);
248 } catch (err: any) {
249 Logger.verbose(
250 "[BaseSmartContractAccount](getAddress) getSenderAddress err: ",
251 err,
252 );
253 if (err.cause?.data?.errorName === "SenderAddressResult") {
254 Logger.verbose(
255 "[BaseSmartContractAccount](getAddress) entryPoint.getSenderAddress result:",
256 err.cause.data.args[0],
257 );
258
259 return err.cause.data.args[0] as Address;
260 }
261
262 if (err.details === "Invalid URL") {
263 throw new InvalidRpcUrlError();
264 }
265 }
266
267 throw new GetCounterFactualAddressError();
268};
269
270// [!region toSmartContractAccount]
271export async function toSmartContractAccount<
272 Name extends string = string,
273 TTransport extends Transport = Transport,
274 TChain extends Chain = Chain,
275 TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
276>({
277 transport,
278 chain,
279 entryPoint,
280 source,
281 accountAddress,
282 getAccountInitCode,
283 getNonce,
284 signMessage,
285 signTypedData,
286 encodeBatchExecute,
287 encodeExecute,
288 getDummySignature,
289 signUserOperationHash,
290 encodeUpgradeToAndCall,
291}: ToSmartContractAccountParams<
292 Name,
293 TTransport,
294 TChain,
295 TEntryPointVersion
296>): Promise<SmartContractAccount<Name, TEntryPointVersion>>;
297// [!endregion toSmartContractAccount]
298
299/**
300 * Converts an account to a smart contract account and sets up various account-related methods using the provided parameters like transport, chain, entry point, and other utilities.
301 *
302 * @example
303 * ```ts
304 * import { http, type SignableMessage } from "viem";
305 * import { sepolia } from "viem/chains";
306 *
307 * const myAccount = await toSmartContractAccount({
308 * /// REQUIRED PARAMS ///
309 * source: "MyAccount",
310 * transport: http("RPC_URL"),
311 * chain: sepolia,
312 * // The EntryPointDef that your account is com"patible with
313 * entryPoint: getEntryPoint(sepolia, { version: "0.6.0" }),
314 * // This should return a concatenation of your `factoryAddress` and the `callData` for your factory's create account method
315 * getAccountInitCode: async () => "0x{factoryAddress}{callData}",
316 * // an invalid signature that doesn't cause your account to revert during validation
317 * getDummySignature: () => "0x1234...",
318 * // given a UO in the form of {target, data, value} should output the calldata for calling your contract's execution method
319 * encodeExecute: async (uo) => "0xcalldata",
320 * signMessage: async ({ message }: { message: SignableMessage }) => "0x...",
321 * signTypedData: async (typedData) => "0x000",
322 *
323 * /// OPTIONAL PARAMS ///
324 * // if you already know your account's address, pass that in here to avoid generating a new counterfactual
325 * accountAddress: "0xaddressoverride",
326 * // if your account supports batching, this should take an array of UOs and return the calldata for calling your contract's batchExecute method
327 * encodeBatchExecute: async (uos) => "0x...",
328 * // if your contract expects a different signing scheme than the default signMessage scheme, you can override that here
329 * signUserOperationHash: async (hash) => "0x...",
330 * // allows you to define the calldata for upgrading your account
331 * encodeUpgradeToAndCall: async (params) => "0x...",
332 * });
333 * ```
334 *
335 * @param {ToSmartContractAccountParams} params the parameters required for converting to a smart contract account
336 * @param {Transport} params.transport the transport mechanism used for communication
337 * @param {Chain} params.chain the blockchain chain used in the account
338 * @param {EntryPoint} params.entryPoint the entry point of the smart contract
339 * @param {string} params.source the source identifier for the account
340 * @param {Address} [params.accountAddress] the address of the account
341 * @param {() => Promise<Hex>} params.getAccountInitCode a function to get the initial state code of the account
342 * @param {(message: { message: SignableMessage }) => Promise<Hex>} params.signMessage a function to sign a message
343 * @param {(typedDataDefinition: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>} params.signTypedData a function to sign typed data
344 * @param {(transactions: Transaction[]) => Hex} [params.encodeBatchExecute] a function to encode batch transactions
345 * @param {(tx: Transaction) => Hex} params.encodeExecute a function to encode a single transaction
346 * @param {() => Promise<Hex>} params.getDummySignature a function to get a dummy signature
347 * @param {(uoHash: Hex) => Promise<Hex>} [params.signUserOperationHash] a function to sign user operations
348 * @param {(implementationAddress: Address, implementationCallData: Hex) => Hex} [params.encodeUpgradeToAndCall] a function to encode upgrade call
349 * @returns {Promise<SmartContractAccount>} a promise that resolves to a SmartContractAccount object with methods and properties for interacting with the smart contract account
350 */
351export async function toSmartContractAccount(
352 params: ToSmartContractAccountParams,
353): Promise<SmartContractAccount> {
354 const {
355 transport,
356 chain,
357 entryPoint,
358 source,
359 accountAddress,
360 getAccountInitCode,
361 signMessage,
362 signTypedData,
363 encodeExecute,
364 encodeBatchExecute,
365 getNonce,
366 getDummySignature,
367 signUserOperationHash,
368 encodeUpgradeToAndCall,
369 getImplementationAddress,
370 prepareSign: prepareSign_,
371 formatSign: formatSign_,
372 } = params;
373
374 const client = createBundlerClient({
375 // we set the retry count to 0 so that viem doesn't retry during
376 // getting the address. That call always reverts and without this
377 // viem will retry 3 times, making this call very slow
378 transport: (opts) => transport({ ...opts, chain, retryCount: 0 }),
379 chain,
380 });
381
382 const entryPointContract = getContract({
383 address: entryPoint.address,
384 abi: entryPoint.abi,
385 client,
386 });
387
388 const accountAddress_ = await getAccountAddress({
389 client,
390 entryPoint: entryPoint,
391 accountAddress,
392 getAccountInitCode,
393 });
394
395 let deploymentState = DeploymentState.UNDEFINED;
396
397 const getInitCode = async () => {
398 if (deploymentState === DeploymentState.DEPLOYED) {
399 return "0x";
400 }
401 const contractCode = await client.getCode({
402 address: accountAddress_,
403 });
404
405 if ((contractCode?.length ?? 0) > 2) {
406 deploymentState = DeploymentState.DEPLOYED;
407 return "0x";
408 } else {
409 deploymentState = DeploymentState.NOT_DEPLOYED;
410 }
411
412 return getAccountInitCode();
413 };
414
415 const signUserOperationHash_ =
416 signUserOperationHash ??
417 (async (uoHash: Hex) => {
418 return signMessage({ message: { raw: hexToBytes(uoHash) } });
419 });
420
421 const getFactoryAddress = async (): Promise<Address> =>
422 parseFactoryAddressFromAccountInitCode(await getAccountInitCode())[0];
423
424 const getFactoryData = async (): Promise<Hex> =>
425 parseFactoryAddressFromAccountInitCode(await getAccountInitCode())[1];
426
427 const encodeUpgradeToAndCall_ =
428 encodeUpgradeToAndCall ??
429 (() => {
430 throw new UpgradesNotSupportedError(source);
431 });
432
433 const isAccountDeployed = async () => {
434 const initCode = await getInitCode();
435 return initCode === "0x";
436 };
437
438 const getNonce_ =
439 getNonce ??
440 (async (nonceKey = 0n): Promise<bigint> => {
441 return entryPointContract.read.getNonce([
442 accountAddress_,
443 nonceKey,
444 ]) as Promise<bigint>;
445 });
446
447 const account = toAccount({
448 address: accountAddress_,
449 signMessage,
450 signTypedData,
451 signTransaction: () => {
452 throw new SignTransactionNotSupportedError();
453 },
454 });
455
456 const create6492Signature = async (isDeployed: boolean, signature: Hex) => {
457 if (isDeployed) {
458 return signature;
459 }
460
461 const [factoryAddress, factoryCalldata] =
462 parseFactoryAddressFromAccountInitCode(await getAccountInitCode());
463
464 return wrapSignatureWith6492({
465 factoryAddress,
466 factoryCalldata,
467 signature,
468 });
469 };
470
471 const signMessageWith6492 = async (message: { message: SignableMessage }) => {
472 const [isDeployed, signature] = await Promise.all([
473 isAccountDeployed(),
474 account.signMessage(message),
475 ]);
476
477 return create6492Signature(isDeployed, signature);
478 };
479
480 const signTypedDataWith6492 = async <
481 const typedData extends TypedData | Record<string, unknown>,
482 primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
483 >(
484 typedDataDefinition: TypedDataDefinition<typedData, primaryType>,
485 ): Promise<Hex> => {
486 const [isDeployed, signature] = await Promise.all([
487 isAccountDeployed(),
488 account.signTypedData(typedDataDefinition),
489 ]);
490
491 return create6492Signature(isDeployed, signature);
492 };
493
494 const getImplementationAddress_ =
495 getImplementationAddress ??
496 (async () => {
497 const storage = await client.getStorageAt({
498 address: account.address,
499 // This is the default slot for the implementation address for Proxies
500 slot: "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
501 });
502
503 if (storage == null) {
504 throw new FailedToGetStorageSlotError(
505 "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
506 "Proxy Implementation Address",
507 );
508 }
509
510 // The storage slot contains a full bytes32, but we want only the last 20 bytes.
511 // So, slice off the leading `0x` and the first 12 bytes (24 characters), leaving the last 20 bytes, then prefix with `0x`.
512 return `0x${storage.slice(26)}`;
513 });
514
515 if (entryPoint.version !== "0.6.0" && entryPoint.version !== "0.7.0") {
516 throw new InvalidEntryPointError(chain, entryPoint.version);
517 }
518
519 if ((prepareSign_ && !formatSign_) || (!prepareSign_ && formatSign_)) {
520 throw new Error(
521 "Must implement both prepareSign and formatSign or neither",
522 );
523 }
524
525 const prepareSign =
526 prepareSign_ ??
527 (() => {
528 throw new Error("prepareSign not implemented");
529 });
530
531 const formatSign =
532 formatSign_ ??
533 (() => {
534 throw new Error("formatSign not implemented");
535 });
536
537 return {
538 ...account,
539 source,
540 // TODO: I think this should probably be signUserOperation instead
541 // and allow for generating the UO hash based on the EP version
542 signUserOperationHash: signUserOperationHash_,
543 getFactoryAddress,
544 getFactoryData,
545 encodeBatchExecute:
546 encodeBatchExecute ??
547 (() => {
548 throw new BatchExecutionNotSupportedError(source);
549 }),
550 encodeExecute,
551 getDummySignature,
552 getInitCode,
553 encodeUpgradeToAndCall: encodeUpgradeToAndCall_,
554 getEntryPoint: () => entryPoint,
555 isAccountDeployed,
556 getAccountNonce: getNonce_,
557 signMessageWith6492,
558 signTypedDataWith6492,
559 getImplementationAddress: getImplementationAddress_,
560 prepareSign,
561 formatSign,
562 };
563}