Account Options
A reference guide to AccountOpts for configuring your own instance of a userop.js Account.
AccountOpts
AccountOpts
AccountOpts
has a flexible interface for allowing an Account
to be configured with any compliant ERC-4337 component.
export interface AccountOpts<A extends Abi, F extends Abi> {
// Required global values
accountAbi: A;
factoryAbi: F;
factoryAddress: Address;
ethClient: PublicClient | JsonRpcProvider;
// Optional global values
bundlerClient?: PublicClient | JsonRpcProvider;
entryPointAddress?: Address;
salt?: bigint;
waitTimeoutMs?: number;
waitIntervalMs?: number;
// Required hook methods
setFactoryData: Hooks.SetFactoryDataFunc<F>;
requestSignature: Hooks.RequestSignatureFunc;
// Optional hook methods
requestGasPrice?: Hooks.RequestGasPriceFunc;
requestGasValues?: Hooks.RequestGasValuesFunc;
requestPaymaster?: Hooks.RequestPaymasterFunc;
onBuild?: Hooks.OnBuildFunc;
}
Required global values
accountAbi
accountAbi
This is a JSON ABI of the Smart Account's implementation contract. Ensure your ABI uses TypeScript's const
assertion.
const ACCOUNT_ABI = [
// ABI here...
] as const
This ABI will allow your Account
instance to provide intelligent typing on encodeCallData
. For example, if your smart account has the following function:
function execute(address dest, uint256 value, bytes calldata func) external
Then, Account
will be able to enforce proper types in your application:
const build = await acc
.encodeCallData("execute", [TO_ADDRESS, VALUE, "0x"])
.buildUserOperation();
Providing an unknown function name or invalid parameters will cause a build time error.
factoryAbi
factoryAbi
This is a JSON ABI of the Smart Account's factory contract. Ensure your ABI uses TypeScript's const
assertion.
const FACTORY_ABI = [
// ABI here...
] as const
This ABI will allow your Account
instance to provide intelligent typing on for the setFactoryData
hook.
factoryAddress
factoryAddress
The address for your Smart Account's factory contract in the type 0x${string}
. This allows the Account
instance to properly set the address portion of a User Operation's initCode
.
ethClient
ethClient
An instance of either a viem PublicClient
or ethers JsonRpcProvider
. Used to call RPC methods on both the node and bundler.
Optional global values
bundlerClient
bundlerClient
Defaults to ethClient
.
An instance of either a viem PublicClient
or ethers JsonRpcProvider
. Used to call RPC methods for the bundler only. This is useful if you seperate providers for node and bundler.
entryPointAddress
entryPointAddress
Defaults to the canonical EntryPoint address (see 🗺️ Entity Addresses).
In certain cases where the EntryPoint address might be different (e.g. app chains using different deployers), this can be used to override the value. Note that, the interface will still assume v0.6 of the canonical EntryPoint.
salt
salt
Defaults to 0n
.
A big int value passed to the setFactoryData
hook. This allows use cases for a single verification method to control multiple accounts.
waitTimeoutMs
waitTimeoutMs
Defaults to 60,000
(1 minute).
A timeout value in milliseconds used by the wait
function after sending a User Operation and waiting for it to be included.
waitIntervalMs
waitIntervalMs
Defaults to 3,000
(3 seconds).
An interval value in milliseconds used by the wait
function after sending a User Operation to determine how ofter to poll for a User Operation receipt.
Required hook functions
setFactoryData
setFactoryData
The setFactoryData
hook is any arbitrary function with the following type that builds from the abitype package:
import {
ExtractAbiFunctionNames,
ExtractAbiFunction,
AbiParametersToPrimitiveTypes,
} from "abitype";
type SetFactoryDataFunc<F extends Abi> = (
salt: bigint,
encoder: <M extends ExtractAbiFunctionNames<F>>(
method: M,
inputs: AbiParametersToPrimitiveTypes<ExtractAbiFunction<F, M>["inputs"]>,
) => Hex,
) => Promise<Hex>;
To simplify, the hook is a function that receives a big int salt
and an encoder
function as input and asynchronously outputs a hex
string that is the callData
to the factory contract for deploying a new Smart Account.
The encoder
is a helper function that assists us in encoding the correct callData
to send to the factory contract. It enforces strong types based on the factory ABI (F
in the generic type).
Here is a concrete example using SimpleAccount
factory and the type definition V06.Account.Hooks.SetFactoryDataFunc
:
import { V06 } from "userop"
const setFactoryData: V06.Account.Hooks.SetFactoryDataFunc<
typeof FactoryAbi
> = async (salt, encoder) => {
return encoder("createAccount", [OWNER_ADDRESS, salt]);
};
Providing an unknown function name or invalid parameters will cause a build time error.
requestSignature
requestSignature
The requestSignature
hook is any arbitrary function with the type:
type RequestSignatureFunc = (
type: "dummy" | "final",
message: Hex,
) => Promise<Hex>;
The function is used by the Account
instance to generate a User Operation signature given a type
and message
. A message
will usually be a hex
string of a userOpHash
.
The type
input can have a value of dummy
or final
. A dummy
type indicates the signature needs to be structurally correct but does not need to be valid. These signatures are used for simulation such as estimating gas limits. A final
type is required to be structurally correct and valid. It is used when sending a complete User Operation on-chain.
Here is a concrete example using SimpleAccount
, an ethers Signer
, and the type definition V06.Account.Hooks.RequestSignatureFunc
:
const withEthersSigner = (
signer: ethers.Signer,
): V06.Account.Hooks.RequestSignatureFunc => {
// Dummy signatures can be signed by any key.
// It only needs to be structurally correct for simulation.
const dummy = ethers.Wallet.createRandom();
return async (type, message) => {
if (type === "dummy") {
return dummy.signMessage(ethers.getBytes(message));
}
return signer.signMessage(ethers.getBytes(message);
};
};
Optional hook functions
requestGasPrice
requestGasPrice
Defaults to V06.Hooks.RequestGasPrice.withEthClient(ethClient)
The requestGasPrice
hook is any arbitrary function with the type:
type RequestGasPriceFunc = () => Promise<
Pick<EntryPoint.UserOperation, "maxFeePerGas" | "maxPriorityFeePerGas">
>;
The function asynchronously returns an object with big int values for maxFeePerGas
and maxPriorityFeePerGas
which are appended to the User Operation. If omitted the default behaviour is to apply EIP-1559 gas fee logic and fallback to legacy gas price for non-supporting networks. It will use the given ethClient
to make the necessary RPC calls.
requestGasValues
requestGasValues
Defaults to V06.Hooks.RequestGasValues.withEthClient(ethClient)
The requestGasValues
hook is any arbitrary function with the type:
type RequestGasValuesFunc = (
userop: EntryPoint.UserOperation,
entryPoint: Address,
stateOverrideSet?: RpcStateOverride,
) => Promise<
Pick<
EntryPoint.UserOperation,
"preVerificationGas" | "verificationGasLimit" | "callGasLimit"
>
>;
The function receives a User Operation, EntryPoint address, and optional state override set as input and asynchronously returns an object with big int values for preVerificationGas
, verificationGasLimit
, and callGasLimit
. If omitted, the default behaviour is to use either the given ethClient
or bundlerClient
to call eth_estimateUserOperationGas
.
requestPaymaster
requestPaymaster
Defualts to nil
The requestPaymaster
hook is any arbitrary function with the type:
type RequestPaymasterFunc = (
userop: EntryPoint.UserOperation,
entryPoint: Address,
) => Promise<
Pick<EntryPoint.UserOperation, "paymasterAndData"> &
Partial<EntryPoint.UserOperation>
>;
The function receives a User Operation and EntryPoint address as input and asynchronously returns an object with the paymasterAndData
hex
string along with any optional User Operation field required by the Paymaster to be used. If omitted, the default behaviour is for the Account
to not use a Paymaster.
onBuild
onBuild
Defualts to nil
The onbuild
hook is any arbitrary function with the type:
type OnBuildFunc = (userop: EntryPoint.UserOperation) => void;
It is called once the final User Operation has been built. It is useful for initiating any action before sending such as logging it for debugging purposes. If omitted, it will result in a no op.
Updated 9 months ago