Skip to main content

Safe Account V2

Safe Account V2 uses the original Safe Singleton and adds ERC-4337 functionality using a module/fallback handler.

The V2 contracts, known as the SafeAccountV0_2_0 class in AbstractionKit, supports EntryPoint v0.6.

Import

import { SafeAccountV0_2_0 as SafeAccount } from "abstractionkit"; 

How to use

Initialize a new Safe Account and calculate its address:

const ownerPublicAddress = "0xBdbc5FBC9cA8C3F514D073eC3de840Ac84FC6D31"; // Safe owner pub address
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);

const accountAddress = smartAccount.accountAddress;

Methods

The Essentials methods provides all necessary functionalities with support for overrides, offering a streamlined approach.

initializeNewAccount

Initilizes a new SafeAccount class given a list of owners of public address(es). Only need to be called on the first transaction when the account has not been deployed yet.

Usage

In this example, we initiate a single owner account.

example.ts
import { SafeAccountV0_2_0 as SafeAccount } from "abstractionkit";

const ownerPublicAddress = "0xBdbc5FBC9cA8C3F514D073eC3de840Ac84FC6D31";
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);

console.log("Account address (sender): " + smartAccount.accountAddress);
Example Response
Account address(sender) : 0x1a02592A3484c2077d2E5D24482497F85e1980C6

Source code

initializeNewAccount

createUserOperation

This method determines the nonce, fetch the gas prices, estimate gas limits and return a useroperation to be signed. You can override any of these values using the overrides parameter.

Usage

This example mints the same NFT twice in a single useroperation

example.ts
import { MetaTransaction } from "abstractionkit";

const jsonRpcNodeProvider = "https://rpc2.sepolia.org";
const bundlerUrl = "https://api.candide.dev/bundler/version/network/YOUR_API_KEY";

const transaction: MetaTransaction = {
to: "0xD9de104e3386d9A45a61BcE269c43E48B534e4E7", // NFT contract address
value: 0n,
data: "0x1249c58b", // mint()
}

let userOperation = await smartAccount.createUserOperation(
[transaction, transaction], // batch transactions to mint 2 NFTs
jsonRpcNodeProvider,
bundlerUrl,
)

console.log(userOperation);
Example Response
{
sender: '0x44e3cb9acd92ab055d3251994352bb8fe0e20879',
nonce: 1n,
initCode: '0x',
callData: '0x541d63c800000000000000000000000038869bf66a61cf6bdb996a6ae40d5853fd43b52600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001048d80ff0a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b200d9de104e3386d9a45a61bce269c43e48b534e4e7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041249c58b00d9de104e3386d9a45a61bce269c43e48b534e4e7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041249c58b000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 95085n,
verificationGasLimit: 62187n,
preVerificationGas: 46156n,
maxFeePerGas: 1625933544n,
maxPriorityFeePerGas: 1200000000n,
paymasterAndData: '0x',
signature: '0x00000000000000000000000041c6297bd9573e8d979a272db4f6576a98f639a7e6874055a627769401dc46d01143551ccaa473364ace4340ec395c546dccb725e1eac2639ecef443d229f0071b'
}

Source code

createUserOperation

signUserOperation

This method takes a userOperation, the private keys of the owner of the account, and the chainId and returns the signature field.

example.ts
const chainId = BigInt("11155111"); // sepolia chain ID
const privateKey = "0x4cad764980d84fc6684ca839cae2c78be5432e292fa98416e11687ceb9096a03";
const userOperation = {..}

const signature = smartAccount.signUserOperation(
userOperation,
[privateKey],
chainId,
);

console.log(signature);
Example Response
0x00000000000000000000000041c6297bd9573e8d979a272db4f6576a98f639a7e6874055a627769401dc46d01143551ccaa473364ace4340ec395c546dccb725e1eac2639ecef443d229f0071b

Source code

signUserOperation

sendUserOperation

This method sends the userop to the bundler to be executed onchain. It returns a promise SendUseroperationResponse object to confirm the on-chain inclusion of the userop

example.ts
const sendUserOperationResponse = await smartAccount.sendUserOperation(userOperation, bundlerUrl)

console.log("sendUserOperationResponse: ". sendUserOperationResponse);
console.log("Useroperation sent. Waiting to be included...");

const receipt = await sendUserOperationResponse.included()

console.log("receipt: ", receipt);
Example Response
sendUserOperationResponse: {
userOperationHash: '0x61b3e2c57ad7ad1ae788f0ac84c79b28aab8aeaf872be173cadc72ab8b3d4418',
bundler: { rpcUrl: 'https://api.candide.dev/bundler/version/network/YOUR_API_KEY' },
entrypointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'
}

Useroperation sent. Waiting to be included...

receipt: {
userOpHash: '0x61b3e2c57ad7ad1ae788f0ac84c79b28aab8aeaf872be173cadc72ab8b3d4418',
entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
sender: '0x44e3cb9acd92ab055d3251994352bb8fe0e20879',
nonce: '0x2',
paymaster: '0x0000000000000000000000000000000000000000',
actualGasCost: 261844423573004,
actualGasUsed: 185893,
success: true,
logs: '[{"address":"0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789","blockHash":"0xba4e1221571d457b4a01db81be6c3ca8e1dcf0117c2c383425e8379853345a69","blockNumber":"0x4e4d65","data":"0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000ee2567da7e0c000000000000000000000000000000000000000000000000000000000002d625","logIndex":"0x140","removed":false,"topics":["0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f","0x61b3e2c57ad7ad1ae788f0ac84c79b28aab8aeaf872be173cadc72ab8b3d4418","0x00000000000000000000000044e3cb9acd92ab055d3251994352bb8fe0e20879","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":"0x00289aec83e4f8a109e2026e9e7f9a122bcf66116b1fc9c48099d668eec49f25","transactionIndex":"0xc5"}]',
receipt: {
blockHash: '0xba4e1221571d457b4a01db81be6c3ca8e1dcf0117c2c383425e8379853345a69',
blockNumber: '0x4e4d65',
from: '0x3cfdc212769c890907bce93d3d8c2c53de6a7a89',
cumulativeGasUsed: '0x1c3ffde',
gasUsed: '0x2dbfd',
logs: '[{"address":"0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789","blockHash":"0xba4e1221571d457b4a01db81be6c3ca8e1dcf0117c2c383425e8379853345a69","blockNumber":"0x4e4d65","data":"0x00000000000000000000000000000000000000000000000000013cd7ed43f8b8","logIndex":"0x13a","removed":false,"topics":["0x2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4","0x00000000000000000000000044e3cb9acd92ab055d3251994352bb8fe0e20879"],"transactionHash":"0x00289aec83e4f8a109e2026e9e7f9a122bcf66116b1fc9c48099d668eec49f25","transactionIndex":"0xc5"},{"address":"0x44e3cb9acd92ab055d3251994352bb8fe0e20879","blockHash":"0xba4e1221571d457b4a01db81be6c3ca8e1dcf0117c2c383425e8379853345a69","blockNumber":"0x4e4d65","data":"0x","logIndex":"0x13b","removed":false,"topics":["0x6895c13664aa4f67288b25d7a21d7aaa34916e355fb9b6fae0a139a9085becb8","0x000000000000000000000000d556564bacf6feac2e26ff70695f8250cea8c29e"],"transactionHash":"0x00289aec83e4f8a109e2026e9e7f9a122bcf66116b1fc9c48099d668eec49f25","transactionIndex":"0xc5"},{"address":"0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789","blockHash":"0xba4e1221571d457b4a01db81be6c3ca8e1dcf0117c2c383425e8379853345a69","blockNumber":"0x4e4d65","data":"0x","logIndex":"0x13c","removed":false,"topics":["0xbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972"],"transactionHash":"0x00289aec83e4f8a109e2026e9e7f9a122bcf66116b1fc9c48099d668eec49f25","transactionIndex":"0xc5"},{"address":"0xd9de104e3386d9a45a61bce269c43e48b534e4e7","blockHash":"0xba4e1221571d457b4a01db81be6c3ca8e1dcf0117c2c383425e8379853345a69","blockNumber":"0x4e4d65","data":"0x","logIndex":"0x13d","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000000000000000000000000000000000000000000000","0x00000000000000000000000044e3cb9acd92ab055d3251994352bb8fe0e20879","0x0000000000000000000000000000000000000000000000000000000000000023"],"transactionHash":"0x00289aec83e4f8a109e2026e9e7f9a122bcf66116b1fc9c48099d668eec49f25","transactionIndex":"0xc5"},{"address":"0xd9de104e3386d9a45a61bce269c43e48b534e4e7","blockHash":"0xba4e1221571d457b4a01db81be6c3ca8e1dcf0117c2c383425e8379853345a69","blockNumber":"0x4e4d65","data":"0x","logIndex":"0x13e","removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000000000000000000000000000000000000000000000","0x00000000000000000000000044e3cb9acd92ab055d3251994352bb8fe0e20879","0x0000000000000000000000000000000000000000000000000000000000000024"],"transactionHash":"0x00289aec83e4f8a109e2026e9e7f9a122bcf66116b1fc9c48099d668eec49f25","transactionIndex":"0xc5"},{"address":"0x44e3cb9acd92ab055d3251994352bb8fe0e20879","blockHash":"0xba4e1221571d457b4a01db81be6c3ca8e1dcf0117c2c383425e8379853345a69","blockNumber":"0x4e4d65","data":"0x","logIndex":"0x13f","removed":false,"topics":["0x6895c13664aa4f67288b25d7a21d7aaa34916e355fb9b6fae0a139a9085becb8","0x000000000000000000000000d556564bacf6feac2e26ff70695f8250cea8c29e"],"transactionHash":"0x00289aec83e4f8a109e2026e9e7f9a122bcf66116b1fc9c48099d668eec49f25","transactionIndex":"0xc5"},{"address":"0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789","blockHash":"0xba4e1221571d457b4a01db81be6c3ca8e1dcf0117c2c383425e8379853345a69","blockNumber":"0x4e4d65","data":"0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000ee2567da7e0c000000000000000000000000000000000000000000000000000000000002d625","logIndex":"0x140","removed":false,"topics":["0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f","0x61b3e2c57ad7ad1ae788f0ac84c79b28aab8aeaf872be173cadc72ab8b3d4418","0x00000000000000000000000044e3cb9acd92ab055d3251994352bb8fe0e20879","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":"0x00289aec83e4f8a109e2026e9e7f9a122bcf66116b1fc9c48099d668eec49f25","transactionIndex":"0xc5"}]',
logsBloom: '0x000000000000100000000000000000000000000000000000000000000000000000080000000000000022080100000000001000000000008000000200000020000000000000020000000000080000000008100000000000000000000000002000020000000a0800000000000000000800000000000000000000000014000200000000000000000000000000000008000040000000000200000000000000000000000000000002000000400000400000000200000000000000000002200008000000000002000000000001000008000000000000000000080800000000000020000040000000000000000000000000000200000000000000000100000000000000',
transactionHash: '0x00289aec83e4f8a109e2026e9e7f9a122bcf66116b1fc9c48099d668eec49f25',
transactionIndex: '0xc5',
effectiveGasPrice: '0xc6e9e20'
}
}

Source code

sendUserOperation

Advanced Methods

The Advanced methods offer fine control and customization, catering to developers who require detailed configurations for their specific requirements.

createAccountAddress

Calculates the Account address from the initial owners

Usage

In this example, we initiate a single owner account.

example.ts
import { SafeAccount } from "abstractionkit";

const ownerPublicAddress = "0xBdbc5FBC9cA8C3F514D073eC3de840Ac84FC6D31";
const safeAddress = SafeAccount.createAccountAddress(
[ownerPublicAddress],
);

console.log("Account address (sender): " + safeAddress);
Example Response
Account address(sender) : 0x1a02592A3484c2077d2E5D24482497F85e1980C6

Source code

getUserOperationEip712Hash

createInitCode

Calculates the intCode needed to deploy the account onchain

Usage

In this example, we initiate a single owner account.

example.ts
import { SafeAccount } from "abstractionkit";

const owner1PublicAddress = "0xBdbc5FBC9cA8C3F514D073eC3de840Ac84FC6D31";
const owner2PublicAddress = "0x4991A5360e5da9BAF62fF644d89F46268e5159eA";
const initCode = SafeAccount.createInitCode([ownerPublicAddress, owner2PublicAddress], 2);

console.log("initCode: ", initCode);
Example Response
initCode: 0x...

Source code

createInitCode

createAccountAddressAndInitCode

Calculates the Safe address and the initCode needed to deploy the account onchain

Usage

In this example, we initiate a single owner account.

example.ts
import { SafeAccountV0_2_0 as SafeAccount } from "abstractionkit";

const ownerPublicAddress = "0xBdbc5FBC9cA8C3F514D073eC3de840Ac84FC6D31";
let [accountAddress, initCode] = SafeAccount.createAccountAddressAndInitCode(
[ownerPublicAddress],
);

console.log("Account address (sender): " + accountAddress);
console.log("initCode: ", initCode);
Example Response
Account address(sender) : 0x1a02592A3484c2077d2E5D24482497F85e1980C6
initCode: 0x...

Source code

createAccountAddressAndInitCode

createInitializerCallData

Creates the initilizer calldata

Usage

import { SafeAccountV0_3_0 as SafeAccount } from "abstractionkit";

const initializeCallData = SafeAccount.createInitializerCallData(
[ownerPublicAddress], // owners
1, //threshold
);
console.log("initializeCallData: " + initializeCallData);
Example Response
initializeCallData: 0xb63e800d000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000002dd68b007b46fbe91b9a7c3eda5a7a1063cb5b47000000000000000000000000000000000000000000000000000000000000014000000000000000000000000075cf11467937ce3f2f357ce24ffc3dbf8fd5c2260000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bdbc5fbc9ca8c3f514d073ec3de840ac84fc6d3100000000000000000000000000000000000000000000000000000000000000648d0dc49f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000075cf11467937ce3f2f357ce24ffc3dbf8fd5c22600000000000000000000000000000000000000000000000000000000

Source

createInitializerCallData

createAccountCallDataSingleTransaction

Encode calldata for a single MetaTransaction to be executed by Safe account

Usage

In this example, we make a transfer of 1 wei to a random address.

example.ts
import { SafeAccountV0_2_0 as SafeAccount } from "abstractionkit";

const ownerPublicAddress = "0xBdbc5FBC9cA8C3F514D073eC3de840Ac84FC6D31";
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);

const callData = smartAccount.createAccountCallDataSingleTransaction({
to: "0x1a02592A3484c2077d2E5D24482497F85e1980C6",
value: 1,
data: "0x",
});

console.log("callData: " + callData);
Example Response
callData : 0xf34308ef000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Source code

createAccountCallDataSingleTransaction

createAccountCallDataBatchTransactions

Encode calldata for a list of MetaTransactions to be executed by Safe account

Usage

In this example, we make a transfer to 2 different random addresses, 1 wei each.

example.ts
import {
SafeAccountV0_2_0 as SafeAccount,
MetaTransaction,
} from "abstractionkit";

const ownerPublicAddress = "0xBdbc5FBC9cA8C3F514D073eC3de840Ac84FC6D31";
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);

const tx1: MetaTransaction = {
to: "0x1a02592A3484c2077d2E5D24482497F85e1980C6",
value: 1,
data: "0x",
};
const tx2: MetaTransaction = {
to: "0x3fe285dcd76bcce4ac92d38a6f2f8e964041e020",
value: 1,
data: "0x",
};

const callData = SafeAccount.createAccountCallDataBatchTransactions([tx1, tx2]);

console.log("callData: " + callData);
Example Response
callData : 0xf34308ef000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Source code

createAccountCallDataBatchTransactions

estimateUserOperationGas

Estimate gas limits for a userOperation

Usage

import { 
SafeAccountV0_2_0 as SafeAccount,
UserOperationV6
} from "abstractionkit";

const bundlerRPC = "https://api.candide.dev/bundler/version/network/YOUR_API_KEY";

const ownerPublicAddress = "0xBdbc5FBC9cA8C3F514D073eC3de840Ac84FC6D31";
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress])

// Use createUserOperation() to help you construct the userOp below
let userOperation: UserOperationV6 = smartAccount.createUserOperation(..);

const [preVerificationGas, verificationGasLimit, callGasLimit] = await estimateUserOperationGas(userOperation, bundlerRPC);
Example Response
[ 46840n, 64545n, 102761n ]

Source code

estimateUserOperationGas

getUserOperationEip712Hash

Create a userOperation eip712 hash

Usage

example
console.log("Hello world");

Source

getUserOperationEip712Hash

formatEip712SignaturesToUseroperationSignature

A static method to format a list of eip712 signatures to a userOperation signature.

Usage

import { SafeAccountV0_2_0 as SafeAccount, EIP712_SAFE_OPERATION_V6_TYPE } from "abstractionkit";
import { Wallet } from "ethers";

const ownerPublicAddress = process.env.PUBLIC_ADDRESS as string;
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);

let userOperation = ... // Use createUserOperation() to help you construct the userOp below

const domain = {
chainId: process.env.CHAIN_ID,
verifyingContract: smartAccount.safe4337ModuleAddress,
};

const types = EIP712_SAFE_OPERATION_V6_TYPE;

// formate according to EIP712 Safe Operation Type
const { sender, ...userOp } = userOperation;
const safeUserOperation = {
...userOp,
safe: userOperation.sender,
validUntil: BigInt(0),
validAfter: BigInt(0),
entryPoint: smartAccount.entrypointAddress,
};

const ownerPrivateKey = process.env.PRIVATE_KEY as string;
const signer = new Wallet(ownerPrivateKey);
const signature = await signer.signTypedData(domain, types, safeUserOperation);
const formatedSig = SafeAccount.formatEip712SignaturesToUseroperationSignature([ownerPublicAddress], [signature]);
userOperation.signature = formatedSig;
Example Response
0x0000000000000000000000006da39f6f7b0d2c0035084d3c313350697b3167ff591a84bf0b4bb4741224b5d226682ec306544c091e2b6535042c900b459282edfe98e393d552963ca8db11731c

Source code

formatEip712SignaturesToUseroperationSignature

isModuleEnabled

Checks if a specified module is enabled for a given Safe account. This function returns a boolean indicating the module's status.

keytypedescription
nodeRpcUrlstringThe JSON-RPC API url for the target chain.
moduleAddressstringThe module address to check if enabled.

createAddOwnerWithThresholdMetaTransactions

Creates a meta-transaction to add a new owner to the Safe account with a specified threshold. This function returns an object containing the meta-transaction details, including the target address, encoded data, and transaction value.

keytypedescription
newOwner
keytypedescription
SignerECDSAPublicAddress | WebauthnPublicKeySigner type which can be either an ECDSA public address or a Webauthn public key
The public address of the new owner to be added
thresholdnumberThe new threshold value for owner confirmations.
overrides
keytypedescription
nodeRpcUrlstring?The JSON-RPC API url for the target chain, to check if the new webauthn owner is deployed or not
eip7212WebAuthnPrecompileVerifierstring?Address for the EIP-7212 WebAuthn precompile verifier.
eip7212WebAuthnContractVerifierstring?Address for the EIP-7212 WebAuthn contract verifier.
webAuthnSignerFactorystring?Address for the WebAuthn signer factory.
webAuthnSignerSingletonstring?Address for the WebAuthn signer singleton.
Optional Overrides for the default values used in the transaction.

createSwapOwnerMetaTransactions

Creates a meta-transaction to swap an owner in the Safe account. If a new owner verifier is not already deployed, it will deploy one and fetch the previous owner automatically. This function returns a promise that resolves to a list of meta-transactions containing the necessary details for the swap and deployment.

keytypedescription
nodeRpcUrlstringThe JSON-RPC API URL for the target chain to fetch the previous owner.
newOwnerSignerThe public address of the new owner to be added.
oldOwnerSignerThe public address of the owner to be replaced.
overrides
keytypedescription
prevOwnerstringIf set, it will be used as the previous owner instead of fetching it.
eip7212WebAuthnPrecompileVerifierstringAddress for the EIP-7212 WebAuthn precompile verifier.
eip7212WebAuthnContractVerifierstringAddress for the EIP-7212 WebAuthn contract verifier.
webAuthnSignerFactorystringAddress for the WebAuthn signer factory.
webAuthnSignerSingletonstringAddress for the WebAuthn signer singleton.
Overrides for the default values used in the transaction.

createRemoveOwnerMetaTransaction

Creates a meta-transaction to remove an owner from the Safe account, fetching the previous owner if not provided. This function returns a promise that resolves to a meta-transaction object containing the necessary details for the removal.

keytypedescription
nodeRpcUrlstringThe JSON-RPC API URL for the target chain to fetch the previous owner.
ownerToDeleteSignerThe public address of the owner to be deleted.
thresholdnumberThe new threshold value for owner confirmations after removal.
overrides
keytypedescription
prevOwnerstringIf set, it will be used as the previous owner instead of fetching it.
eip7212WebAuthnPrecompileVerifierstringAddress for the EIP-7212 WebAuthn precompile verifier.
eip7212WebAuthnContractVerifierstringAddress for the EIP-7212 WebAuthn contract verifier.
webAuthnSignerFactorystringAddress for the WebAuthn signer factory.
webAuthnSignerSingletonstringAddress for the WebAuthn signer singleton.
Optional Overrides for the default values used in the transaction.

Audits