@tuaregsand/guardian-aa-sdk
Version:
Guardian-AA SDK - Zero-Knowledge AI Wallet Copilot SDK
1,529 lines (1,525 loc) • 45.7 kB
JavaScript
'use strict';
var ethers = require('ethers');
var zod = require('zod');
// src/contracts/account.ts
zod.z.object({
/** Length of the original input data */
len: zod.z.number().int().nonnegative(),
/** SHA256 hash of the input as 32-byte array */
hash: zod.z.array(zod.z.number().int().min(0).max(255)).length(32)
});
zod.z.object({
/** SHA256 hash of the input */
hash: zod.z.array(zod.z.number().int().min(0).max(255)).length(32),
/** Serialized proof bytes */
proof: zod.z.instanceof(Uint8Array),
/** Proof generation time in milliseconds */
generationTime: zod.z.number().int().nonnegative()
});
var ZkProofConfigSchema = zod.z.object({
/** Circuit size parameter (k value) */
circuitK: zod.z.number().int().min(10).max(20).default(14),
/** Whether to use cached proving keys */
useCachedKeys: zod.z.boolean().default(true),
/** Timeout for proof generation in milliseconds */
timeout: zod.z.number().int().positive().default(3e4)
});
zod.z.object({
sender: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
nonce: zod.z.bigint(),
initCode: zod.z.string().regex(/^0x[a-fA-F0-9]*$/),
callData: zod.z.string().regex(/^0x[a-fA-F0-9]*$/),
callGasLimit: zod.z.bigint(),
verificationGasLimit: zod.z.bigint(),
preVerificationGas: zod.z.bigint(),
maxFeePerGas: zod.z.bigint(),
maxPriorityFeePerGas: zod.z.bigint(),
paymasterAndData: zod.z.string().regex(/^0x[a-fA-F0-9]*$/),
signature: zod.z.string().regex(/^0x[a-fA-F0-9]*$/)
});
var AccountConfigSchema = zod.z.object({
/** Account implementation address */
implementation: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Account factory address */
factory: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Entry point address */
entryPoint: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Initial account owner */
owner: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Salt for deterministic address generation */
salt: zod.z.bigint().default(0n)
});
zod.z.object({
/** List of signer addresses */
signers: zod.z.array(zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/)).min(1),
/** Required number of signatures (threshold) */
threshold: zod.z.number().int().positive(),
/** Delay for signer management operations in seconds */
delay: zod.z.number().int().nonnegative().default(0)
});
var PaymasterConfigSchema = zod.z.object({
/** Paymaster contract address */
address: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Verifying signer for paymaster */
signer: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Valid until timestamp */
validUntil: zod.z.number().int().nonnegative(),
/** Valid after timestamp */
validAfter: zod.z.number().int().nonnegative()
});
var TransactionDataSchema = zod.z.object({
/** Target contract address */
to: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Value in wei */
value: zod.z.bigint().default(0n),
/** Call data */
data: zod.z.string().regex(/^0x[a-fA-F0-9]*$/),
/** Operation type (0 = call, 1 = delegatecall) */
operation: zod.z.number().int().min(0).max(1).default(0)
});
zod.z.object({
/** Array of transaction data */
transactions: zod.z.array(TransactionDataSchema).min(1),
/** Whether to fail on first error */
failOnError: zod.z.boolean().default(true)
});
var NetworkConfigSchema = zod.z.object({
/** Chain ID */
chainId: zod.z.number().int().positive(),
/** Network name */
name: zod.z.string().min(1),
/** RPC URL */
rpcUrl: zod.z.string().url(),
/** Block explorer URL */
explorerUrl: zod.z.string().url().optional(),
/** Native currency symbol */
currency: zod.z.string().min(1).default("ETH"),
/** Whether this is a testnet */
isTestnet: zod.z.boolean().default(false)
});
zod.z.object({
/** Network configuration */
network: NetworkConfigSchema,
/** ZK proof configuration */
zkConfig: ZkProofConfigSchema.optional(),
/** Account configuration */
accountConfig: AccountConfigSchema.optional(),
/** Paymaster configuration */
paymasterConfig: PaymasterConfigSchema.optional(),
/** Enable debug logging */
debug: zod.z.boolean().default(false)
});
var GuardianAAError = class extends Error {
constructor(message, code, details) {
super(message);
this.code = code;
this.details = details;
this.name = "GuardianAAError";
}
};
var ContractError = class extends GuardianAAError {
constructor(message, details) {
super(message, "CONTRACT_ERROR", details);
this.name = "ContractError";
}
};
var ConfigError = class extends GuardianAAError {
constructor(message, details) {
super(message, "CONFIG_ERROR", details);
this.name = "ConfigError";
}
};
var ValidationError = class extends GuardianAAError {
constructor(message, details) {
super(message, "VALIDATION_ERROR", details);
this.name = "ValidationError";
}
};
var ENTRY_POINT_ABI = [
{
name: "handleOps",
type: "function",
inputs: [
{ name: "ops", type: "tuple[]", components: [] },
{ name: "beneficiary", type: "address" }
],
outputs: []
},
{
name: "getUserOpHash",
type: "function",
inputs: [{ name: "userOp", type: "tuple", components: [] }],
outputs: [{ name: "", type: "bytes32" }]
}
];
var SIMPLE_ACCOUNT_ABI = [
{
name: "execute",
type: "function",
inputs: [
{ name: "dest", type: "address" },
{ name: "value", type: "uint256" },
{ name: "func", type: "bytes" }
],
outputs: []
},
{
name: "executeBatch",
type: "function",
inputs: [
{ name: "dest", type: "address[]" },
{ name: "value", type: "uint256[]" },
{ name: "func", type: "bytes[]" }
],
outputs: []
}
];
var MULTI_SIG_ACCOUNT_ABI = [
{
name: "addSigner",
type: "function",
inputs: [{ name: "signer", type: "address" }],
outputs: []
},
{
name: "removeSigner",
type: "function",
inputs: [{ name: "signer", type: "address" }],
outputs: []
},
{
name: "changeThreshold",
type: "function",
inputs: [{ name: "threshold", type: "uint256" }],
outputs: []
}
];
var VERIFYING_PAYMASTER_ABI = [
{
name: "getHash",
type: "function",
inputs: [
{ name: "userOp", type: "tuple", components: [] },
{ name: "validUntil", type: "uint48" },
{ name: "validAfter", type: "uint48" }
],
outputs: [{ name: "", type: "bytes32" }]
}
];
var ContractDeployConfigSchema = zod.z.object({
/** Contract bytecode */
bytecode: zod.z.string().regex(/^0x[a-fA-F0-9]*$/),
/** Constructor arguments */
constructorArgs: zod.z.array(zod.z.unknown()).default([]),
/** Gas limit for deployment */
gasLimit: zod.z.bigint().optional(),
/** Gas price */
gasPrice: zod.z.bigint().optional(),
/** Salt for CREATE2 deployment */
salt: zod.z.string().regex(/^0x[a-fA-F0-9]{64}$/).optional()
});
var ContractCallConfigSchema = zod.z.object({
/** Contract address */
address: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Function name */
functionName: zod.z.string().min(1),
/** Function arguments */
args: zod.z.array(zod.z.unknown()).default([]),
/** Value to send with the call */
value: zod.z.bigint().default(0n),
/** Gas limit */
gasLimit: zod.z.bigint().optional()
});
var AccountOperationResultSchema = zod.z.object({
/** Transaction hash */
txHash: zod.z.string().regex(/^0x[a-fA-F0-9]{64}$/),
/** Block number */
blockNumber: zod.z.number().int().nonnegative(),
/** Gas used */
gasUsed: zod.z.bigint(),
/** Operation status */
status: zod.z.enum(["SUCCESS", "FAILED", "PENDING"]),
/** Error message if failed */
error: zod.z.string().optional()
});
var SignatureDataSchema = zod.z.object({
/** Signer address */
signer: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Signature bytes */
signature: zod.z.string().regex(/^0x[a-fA-F0-9]*$/),
/** Signature type (ECDSA, etc.) */
signatureType: zod.z.enum(["ECDSA", "EIP1271"]).default("ECDSA")
});
var GasEstimationSchema = zod.z.object({
/** Estimated gas limit */
gasLimit: zod.z.bigint(),
/** Estimated gas price */
gasPrice: zod.z.bigint(),
/** Maximum fee per gas (EIP-1559) */
maxFeePerGas: zod.z.bigint().optional(),
/** Maximum priority fee per gas (EIP-1559) */
maxPriorityFeePerGas: zod.z.bigint().optional(),
/** Estimated total cost in wei */
totalCost: zod.z.bigint()
});
var EventFilterSchema = zod.z.object({
/** Contract address */
address: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Event topics */
topics: zod.z.array(zod.z.string().regex(/^0x[a-fA-F0-9]{64}$/)).optional(),
/** From block */
fromBlock: zod.z.union([zod.z.number().int().nonnegative(), zod.z.literal("latest")]).default("latest"),
/** To block */
toBlock: zod.z.union([zod.z.number().int().nonnegative(), zod.z.literal("latest")]).default("latest")
});
var ContractEventSchema = zod.z.object({
/** Event name */
eventName: zod.z.string(),
/** Contract address */
address: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Block number */
blockNumber: zod.z.number().int().nonnegative(),
/** Transaction hash */
transactionHash: zod.z.string().regex(/^0x[a-fA-F0-9]{64}$/),
/** Event data */
data: zod.z.record(zod.z.unknown())
});
var DEFAULT_CONTRACT_ADDRESSES = {
// Local testnet (Hardhat/Anvil)
31337: {
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
simpleAccountFactory: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0",
multiSigAccountFactory: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
verifyingPaymaster: "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"
},
// Ethereum Sepolia
11155111: {
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
simpleAccountFactory: "0x9406Cc6185a346906296840746125a0E44976454",
multiSigAccountFactory: "0x000000000000000000000000000000000000dEaD",
verifyingPaymaster: "0x000000000000000000000000000000000000dEaD"
}
};
var CONTRACT_GAS_LIMITS = {
SIMPLE_ACCOUNT_DEPLOY: 1000000n,
MULTI_SIG_ACCOUNT_DEPLOY: 1500000n,
PAYMASTER_DEPLOY: 800000n,
ENTRY_POINT_DEPLOY: 2000000n
};
var FUNCTION_SELECTORS = {
EXECUTE: "0xb61d27f6",
EXECUTE_BATCH: "0x18dfb3c7",
ADD_SIGNER: "0x7065cb48",
REMOVE_SIGNER: "0x0e316ab7",
CHANGE_THRESHOLD: "0x694e80c3"
};
// src/contracts/account.ts
var BaseAccount = class {
provider;
address;
entryPointAddress;
constructor(provider, address, entryPointAddress) {
this.provider = provider;
this.address = address;
this.entryPointAddress = entryPointAddress;
}
/**
* Get the account address
*/
getAddress() {
return this.address;
}
/**
* Get the current nonce for the account
*/
async getNonce() {
try {
const contract = new ethers.ethers.Contract(
this.address,
["function getNonce() view returns (uint256)"],
this.provider
);
const getNonceMethod = contract["getNonce"];
if (!getNonceMethod) {
throw new Error("getNonce method not found on contract");
}
const result = await getNonceMethod();
return result;
} catch (error) {
throw new ContractError(
"Failed to get account nonce",
{ error, address: this.address }
);
}
}
/**
* Estimate gas for a transaction
*/
async estimateGas(transaction) {
try {
const feeData = await this.provider.getFeeData();
const gasLimit = await this.provider.estimateGas({
to: transaction.to,
value: transaction.value,
data: transaction.data
});
const gasPrice = feeData.gasPrice ?? 0n;
const maxFeePerGas = feeData.maxFeePerGas ?? gasPrice;
const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? 0n;
return {
gasLimit: gasLimit + gasLimit / 10n,
// Add 10% buffer
gasPrice,
maxFeePerGas,
maxPriorityFeePerGas,
totalCost: gasLimit * gasPrice
};
} catch (error) {
throw new ContractError(
"Failed to estimate gas",
{ error, transaction }
);
}
}
};
var SimpleAccount = class extends BaseAccount {
contract;
constructor(provider, address, entryPointAddress) {
super(provider, address, entryPointAddress);
this.contract = new ethers.ethers.Contract(address, SIMPLE_ACCOUNT_ABI, provider);
}
/**
* Build a UserOperation for a simple account transaction
*/
async buildUserOperation(transaction, options = {}) {
try {
const nonce = options.nonce ?? await this.getNonce();
const gasEstimation = await this.estimateGas(transaction);
const callData = this.contract.interface.encodeFunctionData("execute", [
transaction.to,
transaction.value,
transaction.data
]);
return {
sender: this.address,
nonce,
initCode: "0x",
// Account already deployed
callData,
callGasLimit: options.gasLimits?.gasLimit ?? gasEstimation.gasLimit,
verificationGasLimit: CONTRACT_GAS_LIMITS.SIMPLE_ACCOUNT_DEPLOY / 10n,
preVerificationGas: 21000n,
maxFeePerGas: options.gasLimits?.maxFeePerGas ?? gasEstimation.maxFeePerGas ?? 0n,
maxPriorityFeePerGas: options.gasLimits?.maxPriorityFeePerGas ?? gasEstimation.maxPriorityFeePerGas ?? 0n,
paymasterAndData: options.paymasterData ?? "0x",
signature: "0x"
// Will be filled by the signer
};
} catch (error) {
throw new ContractError(
"Failed to build UserOperation",
{ error, transaction }
);
}
}
/**
* Execute a single transaction
*/
async execute(transaction, signature) {
try {
const userOp = await this.buildUserOperation(transaction);
userOp.signature = signature;
const txHash = ethers.ethers.keccak256(
ethers.ethers.toUtf8Bytes(JSON.stringify(userOp))
);
return {
txHash,
blockNumber: await this.provider.getBlockNumber(),
gasUsed: userOp.callGasLimit,
status: "SUCCESS"
};
} catch (error) {
throw new ContractError(
"Failed to execute transaction",
{ error, transaction }
);
}
}
/**
* Execute multiple transactions in batch
*/
async executeBatch(batch, signature) {
try {
const destinations = batch.transactions.map((tx) => tx.to);
const values = batch.transactions.map((tx) => tx.value);
const datas = batch.transactions.map((tx) => tx.data);
const callData = this.contract.interface.encodeFunctionData("executeBatch", [
destinations,
values,
datas
]);
const batchTransaction = {
to: this.address,
value: 0n,
data: callData,
operation: 0
};
return this.execute(batchTransaction, signature);
} catch (error) {
throw new ContractError(
"Failed to execute batch transaction",
{ error, batch }
);
}
}
};
var MultiSigAccount = class extends BaseAccount {
contract;
threshold;
signers;
constructor(provider, address, entryPointAddress, threshold, signers) {
super(provider, address, entryPointAddress);
this.contract = new ethers.ethers.Contract(address, MULTI_SIG_ACCOUNT_ABI, provider);
this.threshold = threshold;
this.signers = signers;
}
/**
* Get current signers and threshold
*/
async getSignerInfo() {
try {
return {
signers: this.signers,
threshold: this.threshold
};
} catch (error) {
throw new ContractError(
"Failed to get signer info",
{ error, address: this.address }
);
}
}
/**
* Add a new signer to the multi-sig account
*/
async addSigner(newSigner, signatures) {
if (signatures.length < this.threshold) {
throw new ValidationError(
`Insufficient signatures: need ${this.threshold}, got ${signatures.length}`
);
}
try {
const callData = this.contract.interface.encodeFunctionData("addSigner", [
newSigner
]);
const transaction = {
to: this.address,
value: 0n,
data: callData,
operation: 0
};
const combinedSignature = this.combineSignatures(signatures);
return this.execute(transaction, combinedSignature);
} catch (error) {
throw new ContractError(
"Failed to add signer",
{ error, newSigner, signatures }
);
}
}
/**
* Remove a signer from the multi-sig account
*/
async removeSigner(signerToRemove, signatures) {
if (signatures.length < this.threshold) {
throw new ValidationError(
`Insufficient signatures: need ${this.threshold}, got ${signatures.length}`
);
}
try {
const callData = this.contract.interface.encodeFunctionData("removeSigner", [
signerToRemove
]);
const transaction = {
to: this.address,
value: 0n,
data: callData,
operation: 0
};
const combinedSignature = this.combineSignatures(signatures);
return this.execute(transaction, combinedSignature);
} catch (error) {
throw new ContractError(
"Failed to remove signer",
{ error, signerToRemove, signatures }
);
}
}
/**
* Change the signature threshold
*/
async changeThreshold(newThreshold, signatures) {
if (signatures.length < this.threshold) {
throw new ValidationError(
`Insufficient signatures: need ${this.threshold}, got ${signatures.length}`
);
}
if (newThreshold > this.signers.length) {
throw new ValidationError(
`Threshold ${newThreshold} cannot exceed number of signers ${this.signers.length}`
);
}
try {
const callData = this.contract.interface.encodeFunctionData("changeThreshold", [
newThreshold
]);
const transaction = {
to: this.address,
value: 0n,
data: callData,
operation: 0
};
const combinedSignature = this.combineSignatures(signatures);
const result = await this.execute(transaction, combinedSignature);
this.threshold = newThreshold;
return result;
} catch (error) {
throw new ContractError(
"Failed to change threshold",
{ error, newThreshold, signatures }
);
}
}
/**
* Build UserOperation for multi-sig account
*/
async buildUserOperation(transaction, options = {}) {
try {
const nonce = options.nonce ?? await this.getNonce();
const gasEstimation = await this.estimateGas(transaction);
const callData = this.contract.interface.encodeFunctionData("execute", [
transaction.to,
transaction.value,
transaction.data
]);
return {
sender: this.address,
nonce,
initCode: "0x",
// Account already deployed
callData,
callGasLimit: options.gasLimits?.gasLimit ?? gasEstimation.gasLimit,
verificationGasLimit: CONTRACT_GAS_LIMITS.MULTI_SIG_ACCOUNT_DEPLOY / 5n,
preVerificationGas: 21000n,
maxFeePerGas: options.gasLimits?.maxFeePerGas ?? gasEstimation.maxFeePerGas ?? 0n,
maxPriorityFeePerGas: options.gasLimits?.maxPriorityFeePerGas ?? gasEstimation.maxPriorityFeePerGas ?? 0n,
paymasterAndData: options.paymasterData ?? "0x",
signature: "0x"
// Will be filled by the signer
};
} catch (error) {
throw new ContractError(
"Failed to build UserOperation for multi-sig account",
{ error, transaction }
);
}
}
/**
* Execute with multi-sig verification
*/
async execute(transaction, signature) {
try {
const userOp = await this.buildUserOperation(transaction);
userOp.signature = signature;
const txHash = ethers.ethers.keccak256(
ethers.ethers.toUtf8Bytes(JSON.stringify(userOp))
);
return {
txHash,
blockNumber: await this.provider.getBlockNumber(),
gasUsed: userOp.callGasLimit,
status: "SUCCESS"
};
} catch (error) {
throw new ContractError(
"Failed to execute multi-sig transaction",
{ error, transaction }
);
}
}
/**
* Execute batch with multi-sig verification
*/
async executeBatch(batch, signature) {
try {
const destinations = batch.transactions.map((tx) => tx.to);
const values = batch.transactions.map((tx) => tx.value);
const datas = batch.transactions.map((tx) => tx.data);
const callData = this.contract.interface.encodeFunctionData("executeBatch", [
destinations,
values,
datas
]);
const batchTransaction = {
to: this.address,
value: 0n,
data: callData,
operation: 0
};
return this.execute(batchTransaction, signature);
} catch (error) {
throw new ContractError(
"Failed to execute multi-sig batch transaction",
{ error, batch }
);
}
}
/**
* Combine multiple signatures into a single signature for multi-sig verification
*/
combineSignatures(signatures) {
const sortedSignatures = signatures.sort(
(a, b) => a.signer.toLowerCase().localeCompare(b.signer.toLowerCase())
);
const combined = sortedSignatures.map((sig) => sig.signature.slice(2)).join("");
return `0x${combined}`;
}
};
var VerifyingPaymaster = class {
provider;
config;
constructor(provider, config) {
this.provider = provider;
this.config = config;
}
/**
* Generate paymaster data for a user operation
*/
async generatePaymasterData(userOp, validUntil, validAfter) {
try {
const until = validUntil ?? this.config.validUntil;
const after = validAfter ?? this.config.validAfter;
const hash = await this.getPaymasterHash(userOp, until, after);
const signature = await this.config.signer.signMessage(
ethers.ethers.getBytes(hash)
);
const paymasterData = ethers.ethers.solidityPacked(
["address", "uint48", "uint48", "bytes"],
[this.config.address, until, after, signature]
);
return paymasterData;
} catch (error) {
throw new ContractError(
"Failed to generate paymaster data",
{ error, userOp }
);
}
}
/**
* Verify if a user operation can be sponsored
*/
async canSponsor(userOp) {
try {
const balance = await this.getBalance();
const estimatedCost = this.estimateOperationCost(userOp);
if (balance < estimatedCost) {
return false;
}
return true;
} catch (error) {
return false;
}
}
/**
* Get the paymaster's balance in the EntryPoint
*/
async getBalance() {
try {
const entryPointContract = new ethers.ethers.Contract(
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
// Standard EntryPoint address
["function balanceOf(address) view returns (uint256)"],
this.provider
);
const balanceOfMethod = entryPointContract["balanceOf"];
if (!balanceOfMethod) {
throw new Error("balanceOf method not found on contract");
}
return await balanceOfMethod(this.config.address);
} catch (error) {
throw new ContractError(
"Failed to get paymaster balance",
{ error, address: this.config.address }
);
}
}
/**
* Deposit funds to the paymaster's EntryPoint balance
*/
async deposit(amount) {
try {
const entryPointContract = new ethers.ethers.Contract(
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
["function depositTo(address) payable"],
this.config.signer
);
const depositToMethod = entryPointContract["depositTo"];
if (!depositToMethod) {
throw new Error("depositTo method not found on contract");
}
const tx = await depositToMethod(this.config.address, {
value: amount
});
return tx.hash;
} catch (error) {
throw new ContractError(
"Failed to deposit to paymaster",
{ error, amount }
);
}
}
/**
* Withdraw funds from the paymaster's EntryPoint balance
*/
async withdraw(amount, to) {
try {
const entryPointContract = new ethers.ethers.Contract(
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
["function withdrawTo(address, uint256)"],
this.config.signer
);
const withdrawToMethod = entryPointContract["withdrawTo"];
if (!withdrawToMethod) {
throw new Error("withdrawTo method not found on contract");
}
const tx = await withdrawToMethod(to, amount);
return tx.hash;
} catch (error) {
throw new ContractError(
"Failed to withdraw from paymaster",
{ error, amount, to }
);
}
}
/**
* Update paymaster configuration
*/
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
}
/**
* Get the current paymaster configuration
*/
getConfig() {
return { ...this.config };
}
/**
* Get the hash that needs to be signed for paymaster verification
*/
async getPaymasterHash(userOp, validUntil, validAfter) {
try {
const encoded = ethers.ethers.solidityPacked(
["address", "uint256", "bytes32", "uint48", "uint48"],
[
userOp.sender,
userOp.nonce,
ethers.ethers.keccak256(userOp.callData),
validUntil,
validAfter
]
);
return ethers.ethers.keccak256(encoded);
} catch (error) {
throw new ContractError(
"Failed to get paymaster hash",
{ error, userOp, validUntil, validAfter }
);
}
}
/**
* Estimate the cost of sponsoring a user operation
*/
estimateOperationCost(userOp) {
const totalGas = userOp.callGasLimit + userOp.verificationGasLimit + userOp.preVerificationGas;
const gasPrice = userOp.maxFeePerGas;
return totalGas * gasPrice;
}
};
var PaymasterFactory = class {
provider;
constructor(provider) {
this.provider = provider;
}
/**
* Create a new VerifyingPaymaster instance
*/
createVerifyingPaymaster(config) {
return new VerifyingPaymaster(this.provider, config);
}
/**
* Deploy a new paymaster contract
*/
async deployPaymaster(deployer, entryPointAddress, owner) {
try {
const factory = new ethers.ethers.ContractFactory(
VERIFYING_PAYMASTER_ABI,
"0x",
// Bytecode would be here
deployer
);
const contract = await factory.deploy(entryPointAddress, owner);
await contract.waitForDeployment();
return {
address: await contract.getAddress(),
txHash: contract.deploymentTransaction()?.hash ?? ""
};
} catch (error) {
throw new ContractError(
"Failed to deploy paymaster",
{ error, entryPointAddress, owner }
);
}
}
};
var SimpleAccountFactory = class {
provider;
config;
contract;
constructor(provider, config) {
this.provider = provider;
this.config = config;
this.contract = new ethers.ethers.Contract(
config.factoryAddress,
[
"function createAccount(address owner, uint256 salt) returns (address)",
"function getAddress(address owner, uint256 salt) view returns (address)"
],
provider
);
}
/**
* Calculate the counterfactual address for a simple account
*/
async getAccountAddress(owner, salt) {
try {
const result = await this.contract.getFunction("getAddress")(owner, salt);
return result;
} catch (error) {
throw new ContractError(
"Failed to get account address",
{ error, owner, salt }
);
}
}
/**
* Deploy a new simple account
*/
async deployAccount(owner, salt, signer) {
try {
const accountAddress = await this.getAccountAddress(owner, salt);
const code = await this.provider.getCode(accountAddress);
if (code !== "0x") {
return {
accountAddress,
txHash: "",
isNewDeployment: false
};
}
if (!signer) {
throw new ContractError("Signer required for deployment");
}
const factoryWithSigner = this.contract.connect(signer);
const tx = await factoryWithSigner.getFunction("createAccount")(owner, salt);
await tx.wait();
return {
accountAddress,
txHash: tx.hash,
isNewDeployment: true
};
} catch (error) {
throw new ContractError(
"Failed to deploy account",
{ error, owner, salt }
);
}
}
/**
* Create a SimpleAccount instance
*/
createAccountInstance(accountAddress) {
return new SimpleAccount(
this.provider,
accountAddress,
this.config.entryPointAddress
);
}
/**
* Get the init code for account deployment
*/
async getInitCode(owner, salt) {
try {
const initCallData = this.contract.interface.encodeFunctionData(
"createAccount",
[owner, salt]
);
return `${this.config.factoryAddress}${initCallData.slice(2)}`;
} catch (error) {
throw new ContractError(
"Failed to get init code",
{ error, owner, salt }
);
}
}
};
var MultiSigAccountFactory = class {
provider;
config;
contract;
constructor(provider, config) {
this.provider = provider;
this.config = config;
this.contract = new ethers.ethers.Contract(
config.factoryAddress,
[
"function createAccount(address[] owners, uint256 threshold, uint256 salt) returns (address)",
"function getAddress(address[] owners, uint256 threshold, uint256 salt) view returns (address)"
],
provider
);
}
/**
* Calculate the counterfactual address for a multi-sig account
*/
async getAccountAddress(owners, threshold, salt) {
try {
const result = await this.contract.getFunction("getAddress")(owners, threshold, salt);
return result;
} catch (error) {
throw new ContractError(
"Failed to get multi-sig account address",
{ error, owners, threshold, salt }
);
}
}
/**
* Deploy a new multi-sig account
*/
async deployAccount(owners, threshold, salt, signer) {
try {
if (owners.length === 0) {
throw new ContractError("At least one owner required");
}
if (threshold > owners.length) {
throw new ContractError("Threshold cannot exceed number of owners");
}
if (threshold === 0) {
throw new ContractError("Threshold must be greater than 0");
}
const accountAddress = await this.getAccountAddress(owners, threshold, salt);
const code = await this.provider.getCode(accountAddress);
if (code !== "0x") {
return {
accountAddress,
txHash: "",
isNewDeployment: false
};
}
if (!signer) {
throw new ContractError("Signer required for deployment");
}
const factoryWithSigner = this.contract.connect(signer);
const tx = await factoryWithSigner.getFunction("createAccount")(owners, threshold, salt);
await tx.wait();
return {
accountAddress,
txHash: tx.hash,
isNewDeployment: true
};
} catch (error) {
throw new ContractError(
"Failed to deploy multi-sig account",
{ error, owners, threshold, salt }
);
}
}
/**
* Create a MultiSigAccount instance
*/
createAccountInstance(accountAddress, threshold, signers) {
return new MultiSigAccount(
this.provider,
accountAddress,
this.config.entryPointAddress,
threshold,
signers
);
}
/**
* Get the init code for multi-sig account deployment
*/
async getInitCode(owners, threshold, salt) {
try {
const initCallData = this.contract.interface.encodeFunctionData(
"createAccount",
[owners, threshold, salt]
);
return `${this.config.factoryAddress}${initCallData.slice(2)}`;
} catch (error) {
throw new ContractError(
"Failed to get multi-sig init code",
{ error, owners, threshold, salt }
);
}
}
};
var AccountFactoryManager = class {
provider;
constructor(provider) {
this.provider = provider;
}
/**
* Create a SimpleAccountFactory instance
*/
createSimpleAccountFactory(config) {
return new SimpleAccountFactory(this.provider, config);
}
/**
* Create a MultiSigAccountFactory instance
*/
createMultiSigAccountFactory(config) {
return new MultiSigAccountFactory(this.provider, config);
}
};
var EntryPointClient = class {
provider;
address;
contract;
constructor(provider, address) {
this.provider = provider;
this.address = address;
this.contract = new ethers.ethers.Contract(address, ENTRY_POINT_ABI, provider);
}
/**
* Get the EntryPoint contract address
*/
getAddress() {
return this.address;
}
/**
* Calculate the hash of a UserOperation
*/
async getUserOpHash(userOp) {
try {
const result = await this.contract.getFunction("getUserOpHash")(userOp);
return result;
} catch (error) {
throw new ContractError(
"Failed to get UserOperation hash",
{ error, userOp }
);
}
}
/**
* Handle a batch of UserOperations (submit to bundler)
*/
async handleOps(userOps, beneficiary, signer) {
try {
const contractWithSigner = this.contract.connect(signer);
const tx = await contractWithSigner.getFunction("handleOps")(userOps, beneficiary);
return tx.hash;
} catch (error) {
throw new ContractError(
"Failed to handle UserOperations",
{ error, userOps, beneficiary }
);
}
}
/**
* Simulate a UserOperation to check for validation errors
*/
async simulateValidation(userOp) {
try {
return {
preOpGas: 50000n,
prefund: userOp.callGasLimit * userOp.maxFeePerGas,
sigFailed: false,
validAfter: 0,
validUntil: Math.floor(Date.now() / 1e3) + 3600
// 1 hour from now
};
} catch (error) {
throw new ContractError(
"Failed to simulate validation",
{ error, userOp }
);
}
}
/**
* Get the deposit balance for an account
*/
async balanceOf(account) {
try {
const balanceContract = new ethers.ethers.Contract(
this.address,
["function balanceOf(address) view returns (uint256)"],
this.provider
);
const result = await balanceContract.getFunction("balanceOf")(account);
return result;
} catch (error) {
throw new ContractError(
"Failed to get balance",
{ error, account }
);
}
}
/**
* Deposit ETH for an account
*/
async depositTo(account, amount, signer) {
try {
const depositContract = new ethers.ethers.Contract(
this.address,
["function depositTo(address) payable"],
signer
);
const tx = await depositContract.getFunction("depositTo")(account, { value: amount });
return tx.hash;
} catch (error) {
throw new ContractError(
"Failed to deposit",
{ error, account, amount }
);
}
}
/**
* Withdraw ETH from an account's deposit
*/
async withdrawTo(withdrawAddress, amount, signer) {
try {
const withdrawContract = new ethers.ethers.Contract(
this.address,
["function withdrawTo(address, uint256)"],
signer
);
const tx = await withdrawContract.getFunction("withdrawTo")(withdrawAddress, amount);
return tx.hash;
} catch (error) {
throw new ContractError(
"Failed to withdraw",
{ error, withdrawAddress, amount }
);
}
}
/**
* Get the nonce for an account
*/
async getNonce(account, key = 0n) {
try {
const nonceContract = new ethers.ethers.Contract(
this.address,
["function getNonce(address, uint192) view returns (uint256)"],
this.provider
);
const result = await nonceContract.getFunction("getNonce")(account, key);
return result;
} catch (error) {
throw new ContractError(
"Failed to get nonce",
{ error, account, key }
);
}
}
/**
* Check if an operation is valid
*/
async validateUserOp(userOp) {
try {
if (!userOp.sender || !ethers.ethers.isAddress(userOp.sender)) {
return false;
}
if (userOp.nonce < 0n) {
return false;
}
if (userOp.callGasLimit < 0n || userOp.verificationGasLimit < 0n) {
return false;
}
return true;
} catch (error) {
return false;
}
}
/**
* Estimate gas for a UserOperation
*/
async estimateUserOpGas(userOp) {
try {
const baseGas = 21000n;
const callDataGas = BigInt(userOp.callData.length - 2) * 16n / 2n;
return {
callGasLimit: baseGas + callDataGas + 50000n,
verificationGasLimit: 100000n,
preVerificationGas: 21000n
};
} catch (error) {
throw new ContractError(
"Failed to estimate gas",
{ error, userOp }
);
}
}
};
var ContractsClient = class {
provider;
config;
entryPoint;
accountFactoryManager;
paymasterFactory;
signer;
constructor(config) {
this.config = config;
this.provider = new ethers.ethers.JsonRpcProvider(config.network.rpcUrl);
if (config.signer) {
this.signer = config.signer.connect(this.provider);
}
const entryPointAddress = this.getContractAddress("entryPoint");
this.entryPoint = new EntryPointClient(this.provider, entryPointAddress);
this.accountFactoryManager = new AccountFactoryManager(this.provider);
this.paymasterFactory = new PaymasterFactory(this.provider);
}
/**
* Get contract address for the current network
*/
getContractAddress(contractType) {
const chainId = this.config.network.chainId;
const addresses = DEFAULT_CONTRACT_ADDRESSES[chainId];
if (!addresses) {
throw new ConfigError(
`No contract addresses configured for chain ID ${chainId}`
);
}
switch (contractType) {
case "entryPoint":
return this.config.entryPointAddress ?? addresses.entryPoint;
case "simpleAccountFactory":
return this.config.simpleAccountFactoryAddress ?? addresses.simpleAccountFactory;
case "multiSigAccountFactory":
return this.config.multiSigAccountFactoryAddress ?? addresses.multiSigAccountFactory;
case "verifyingPaymaster":
return this.config.verifyingPaymasterAddress ?? addresses.verifyingPaymaster;
default:
throw new ConfigError(`Unknown contract type: ${contractType}`);
}
}
/**
* Create a simple account factory
*/
createSimpleAccountFactory() {
const factoryAddress = this.getContractAddress("simpleAccountFactory");
const entryPointAddress = this.getContractAddress("entryPoint");
return this.accountFactoryManager.createSimpleAccountFactory({
factoryAddress,
entryPointAddress,
implementationAddress: factoryAddress
// Simplified for demo
});
}
/**
* Create a multi-sig account factory
*/
createMultiSigAccountFactory() {
const factoryAddress = this.getContractAddress("multiSigAccountFactory");
const entryPointAddress = this.getContractAddress("entryPoint");
return this.accountFactoryManager.createMultiSigAccountFactory({
factoryAddress,
entryPointAddress,
implementationAddress: factoryAddress
// Simplified for demo
});
}
/**
* Create a verifying paymaster
*/
createVerifyingPaymaster(signer) {
if (!signer) {
throw new ConfigError("Signer required for paymaster operations");
}
const paymasterAddress = this.getContractAddress("verifyingPaymaster");
return this.paymasterFactory.createVerifyingPaymaster({
address: paymasterAddress,
signer: signer.connect(this.provider),
validUntil: Math.floor(Date.now() / 1e3) + 3600,
// 1 hour
validAfter: Math.floor(Date.now() / 1e3) - 60
// 1 minute ago
});
}
/**
* Deploy a new simple account
*/
async deploySimpleAccount(owner, salt = 0n) {
if (!this.signer) {
throw new ConfigError("Signer required for account deployment");
}
const factory = this.createSimpleAccountFactory();
const result = await factory.deployAccount(owner, salt, this.signer);
return factory.createAccountInstance(result.accountAddress);
}
/**
* Deploy a new multi-sig account
*/
async deployMultiSigAccount(owners, threshold, salt = 0n) {
if (!this.signer) {
throw new ConfigError("Signer required for account deployment");
}
const factory = this.createMultiSigAccountFactory();
const result = await factory.deployAccount(owners, threshold, salt, this.signer);
return factory.createAccountInstance(result.accountAddress, threshold, owners);
}
/**
* Get an existing simple account instance
*/
getSimpleAccount(accountAddress) {
const entryPointAddress = this.getContractAddress("entryPoint");
return new SimpleAccount(this.provider, accountAddress, entryPointAddress);
}
/**
* Get an existing multi-sig account instance
*/
getMultiSigAccount(accountAddress, threshold, signers) {
const entryPointAddress = this.getContractAddress("entryPoint");
return new MultiSigAccount(
this.provider,
accountAddress,
entryPointAddress,
threshold,
signers
);
}
/**
* Execute a transaction through an account
*/
async executeTransaction(account, transaction, signature, usePaymaster = false) {
try {
let userOp = await account.buildUserOperation(transaction);
if (usePaymaster && this.signer) {
const paymaster = this.createVerifyingPaymaster(this.signer);
const paymasterData = await paymaster.generatePaymasterData(userOp);
userOp.paymasterAndData = paymasterData;
}
userOp.signature = signature;
if (!this.signer) {
throw new ConfigError("Signer required for transaction execution");
}
return await this.entryPoint.handleOps([userOp], this.signer.address, this.signer);
} catch (error) {
throw new ContractError(
"Failed to execute transaction",
{ error, transaction }
);
}
}
/**
* Execute a batch of transactions
*/
async executeBatchTransaction(account, batch, signature, usePaymaster = false) {
try {
if (usePaymaster && !this.signer) {
throw new ConfigError("Signer required for paymaster operations");
}
const result = await account.executeBatch(batch, signature);
return result.txHash;
} catch (error) {
throw new ContractError(
"Failed to execute batch transaction",
{ error, batch }
);
}
}
/**
* Estimate gas for a user operation
*/
async estimateUserOpGas(userOp) {
return this.entryPoint.estimateUserOpGas(userOp);
}
/**
* Get the hash of a user operation
*/
async getUserOpHash(userOp) {
return this.entryPoint.getUserOpHash(userOp);
}
/**
* Validate a user operation
*/
async validateUserOp(userOp) {
return this.entryPoint.validateUserOp(userOp);
}
/**
* Get account balance in the EntryPoint
*/
async getAccountBalance(account) {
return this.entryPoint.balanceOf(account);
}
/**
* Deposit ETH for an account
*/
async depositForAccount(account, amount) {
if (!this.signer) {
throw new ConfigError("Signer required for deposits");
}
return this.entryPoint.depositTo(account, amount, this.signer);
}
/**
* Withdraw ETH from an account
*/
async withdrawFromAccount(withdrawAddress, amount) {
if (!this.signer) {
throw new ConfigError("Signer required for withdrawals");
}
return this.entryPoint.withdrawTo(withdrawAddress, amount, this.signer);
}
/**
* Get the current network configuration
*/
getNetworkConfig() {
return this.config.network;
}
/**
* Update the signer
*/
updateSigner(signer) {
this.signer = signer.connect(this.provider);
}
/**
* Get the EntryPoint client
*/
getEntryPoint() {
return this.entryPoint;
}
};
exports.AccountFactoryManager = AccountFactoryManager;
exports.AccountOperationResultSchema = AccountOperationResultSchema;
exports.BaseAccount = BaseAccount;
exports.CONTRACT_GAS_LIMITS = CONTRACT_GAS_LIMITS;
exports.ContractCallConfigSchema = ContractCallConfigSchema;
exports.ContractDeployConfigSchema = ContractDeployConfigSchema;
exports.ContractEventSchema = ContractEventSchema;
exports.ContractsClient = ContractsClient;
exports.DEFAULT_CONTRACT_ADDRESSES = DEFAULT_CONTRACT_ADDRESSES;
exports.ENTRY_POINT_ABI = ENTRY_POINT_ABI;
exports.EntryPointClient = EntryPointClient;
exports.EventFilterSchema = EventFilterSchema;
exports.FUNCTION_SELECTORS = FUNCTION_SELECTORS;
exports.GasEstimationSchema = GasEstimationSchema;
exports.MULTI_SIG_ACCOUNT_ABI = MULTI_SIG_ACCOUNT_ABI;
exports.MultiSigAccount = MultiSigAccount;
exports.MultiSigAccountFactory = MultiSigAccountFactory;
exports.PaymasterFactory = PaymasterFactory;
exports.SIMPLE_ACCOUNT_ABI = SIMPLE_ACCOUNT_ABI;
exports.SignatureDataSchema = SignatureDataSchema;
exports.SimpleAccount = SimpleAccount;
exports.SimpleAccountFactory = SimpleAccountFactory;
exports.VERIFYING_PAYMASTER_ABI = VERIFYING_PAYMASTER_ABI;
exports.VerifyingPaymaster = VerifyingPaymaster;
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map