@tuaregsand/guardian-aa-sdk
Version:
Guardian-AA SDK - Zero-Knowledge AI Wallet Copilot SDK
1,758 lines (1,753 loc) • 82.3 kB
JavaScript
import { z } from 'zod';
import { sha256 } from '@noble/hashes/sha256';
import { ethers } from 'ethers';
// src/types/index.ts
var ZkProofOutputSchema = z.object({
/** Length of the original input data */
len: z.number().int().nonnegative(),
/** SHA256 hash of the input as 32-byte array */
hash: z.array(z.number().int().min(0).max(255)).length(32)
});
var ZkProofResultSchema = z.object({
/** SHA256 hash of the input */
hash: z.array(z.number().int().min(0).max(255)).length(32),
/** Serialized proof bytes */
proof: z.instanceof(Uint8Array),
/** Proof generation time in milliseconds */
generationTime: z.number().int().nonnegative()
});
var ZkProofConfigSchema = z.object({
/** Circuit size parameter (k value) */
circuitK: z.number().int().min(10).max(20).default(14),
/** Whether to use cached proving keys */
useCachedKeys: z.boolean().default(true),
/** Timeout for proof generation in milliseconds */
timeout: z.number().int().positive().default(3e4)
});
var UserOperationSchema = z.object({
sender: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
nonce: z.bigint(),
initCode: z.string().regex(/^0x[a-fA-F0-9]*$/),
callData: z.string().regex(/^0x[a-fA-F0-9]*$/),
callGasLimit: z.bigint(),
verificationGasLimit: z.bigint(),
preVerificationGas: z.bigint(),
maxFeePerGas: z.bigint(),
maxPriorityFeePerGas: z.bigint(),
paymasterAndData: z.string().regex(/^0x[a-fA-F0-9]*$/),
signature: z.string().regex(/^0x[a-fA-F0-9]*$/)
});
var AccountConfigSchema = z.object({
/** Account implementation address */
implementation: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Account factory address */
factory: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Entry point address */
entryPoint: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Initial account owner */
owner: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Salt for deterministic address generation */
salt: z.bigint().default(0n)
});
var MultiSigConfigSchema = z.object({
/** List of signer addresses */
signers: z.array(z.string().regex(/^0x[a-fA-F0-9]{40}$/)).min(1),
/** Required number of signatures (threshold) */
threshold: z.number().int().positive(),
/** Delay for signer management operations in seconds */
delay: z.number().int().nonnegative().default(0)
});
var PaymasterConfigSchema = z.object({
/** Paymaster contract address */
address: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Verifying signer for paymaster */
signer: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Valid until timestamp */
validUntil: z.number().int().nonnegative(),
/** Valid after timestamp */
validAfter: z.number().int().nonnegative()
});
var TransactionDataSchema = z.object({
/** Target contract address */
to: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Value in wei */
value: z.bigint().default(0n),
/** Call data */
data: z.string().regex(/^0x[a-fA-F0-9]*$/),
/** Operation type (0 = call, 1 = delegatecall) */
operation: z.number().int().min(0).max(1).default(0)
});
var BatchTransactionSchema = z.object({
/** Array of transaction data */
transactions: z.array(TransactionDataSchema).min(1),
/** Whether to fail on first error */
failOnError: z.boolean().default(true)
});
var NetworkConfigSchema = z.object({
/** Chain ID */
chainId: z.number().int().positive(),
/** Network name */
name: z.string().min(1),
/** RPC URL */
rpcUrl: z.string().url(),
/** Block explorer URL */
explorerUrl: z.string().url().optional(),
/** Native currency symbol */
currency: z.string().min(1).default("ETH"),
/** Whether this is a testnet */
isTestnet: z.boolean().default(false)
});
var SdkConfigSchema = 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: 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 ZkProofError = class extends GuardianAAError {
constructor(message, details) {
super(message, "ZK_PROOF_ERROR", details);
this.name = "ZkProofError";
}
};
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 DEFAULT_GAS_LIMITS = {
VERIFICATION: 70000n,
CALL: 35000n,
CREATION: 1000000n,
PRE_VERIFICATION: 21000n
};
var SUPPORTED_CHAINS = {
ETHEREUM_MAINNET: 1,
ETHEREUM_SEPOLIA: 11155111,
POLYGON_MAINNET: 137,
POLYGON_MUMBAI: 80001,
ARBITRUM_ONE: 42161,
ARBITRUM_SEPOLIA: 421614,
OPTIMISM_MAINNET: 10,
OPTIMISM_SEPOLIA: 11155420
};
var CONTRACT_ADDRESSES = {
// Ethereum Sepolia
[SUPPORTED_CHAINS.ETHEREUM_SEPOLIA]: {
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
accountFactory: "0x9406Cc6185a346906296840746125a0E44976454",
multiSigFactory: "0x000000000000000000000000000000000000dEaD",
verifyingPaymaster: "0x000000000000000000000000000000000000dEaD"
}
// Add more chains as needed
};
var ProverMetricsSchema = z.object({
setupTime: z.number().nonnegative(),
proofTime: z.number().nonnegative(),
verificationTime: z.number().nonnegative(),
proofSize: z.number().int().nonnegative(),
circuitSize: z.number().int().nonnegative()
});
var VerificationStatusSchema = z.enum([
"VALID",
"INVALID",
"ERROR",
"TIMEOUT"
]);
var VerificationResultSchema = z.object({
status: VerificationStatusSchema,
message: z.string().optional(),
verificationTime: z.number().nonnegative().optional(),
publicInputsHash: z.string().optional()
});
var ProverConfigSchema = z.object({
circuitK: z.number().int().min(10).max(20).default(14),
useCachedKeys: z.boolean().default(true),
timeout: z.number().int().positive().default(3e4),
maxInputSize: z.number().int().positive().default(1024 * 1024),
// 1MB
enableBenchmarking: z.boolean().default(false),
logLevel: z.enum(["ERROR", "WARN", "INFO", "DEBUG"]).default("INFO")
});
var ProvingKeyCacheSchema = z.object({
circuitK: z.number().int(),
keyHash: z.string(),
createdAt: z.number().int(),
size: z.number().int(),
isValid: z.boolean()
});
var CircuitStatsSchema = z.object({
numConstraints: z.number().int().nonnegative(),
numAdvice: z.number().int().nonnegative(),
numFixed: z.number().int().nonnegative(),
numInstance: z.number().int().nonnegative(),
degree: z.number().int().nonnegative()
});
var ProofBatchSchema = z.object({
inputs: z.array(z.instanceof(Uint8Array)).min(1),
parallelization: z.boolean().default(true),
batchId: z.string().optional()
});
var BatchProofResultSchema = z.object({
batchId: z.string().optional(),
results: z.array(z.object({
index: z.number().int().nonnegative(),
hash: z.array(z.number().int().min(0).max(255)).length(32),
proof: z.instanceof(Uint8Array),
generationTime: z.number().nonnegative()
})),
totalTime: z.number().nonnegative(),
averageTime: z.number().nonnegative()
});
var ZK_CONSTANTS = {
MAX_CIRCUIT_K: 20,
MIN_CIRCUIT_K: 10,
DEFAULT_CIRCUIT_K: 14,
MAX_INPUT_SIZE: 1024 * 1024,
// 1MB
SHA256_HASH_SIZE: 32,
TARGET_PROOF_TIME_MS: 500,
MAX_PROOF_TIME_MS: 3e4,
PROOF_CACHE_TTL_MS: 24 * 60 * 60 * 1e3
// 24 hours
};
var ZK_ERROR_CODES = {
PROVER_NOT_INITIALIZED: "PROVER_NOT_INITIALIZED",
INVALID_INPUT_SIZE: "INVALID_INPUT_SIZE",
PROOF_GENERATION_FAILED: "PROOF_GENERATION_FAILED",
VERIFICATION_FAILED: "VERIFICATION_FAILED",
TIMEOUT_EXCEEDED: "TIMEOUT_EXCEEDED",
INVALID_CIRCUIT_K: "INVALID_CIRCUIT_K",
PROVING_KEY_NOT_FOUND: "PROVING_KEY_NOT_FOUND",
FFI_ERROR: "FFI_ERROR"
};
// src/zk/prover.ts
var ZkProver = class {
config;
isInitialized = false;
setupTime = 0;
constructor(config = {}) {
this.config = ProverConfigSchema.parse(config);
}
/**
* Initialize the prover (setup proving keys, etc.)
*/
async initialize() {
if (this.isInitialized) {
return;
}
const startTime = performance.now();
try {
await this.setupProvingSystem();
this.setupTime = performance.now() - startTime;
this.isInitialized = true;
if (this.config.enableBenchmarking) {
this.logPerformance("Prover initialization", this.setupTime);
}
} catch (error) {
throw new ZkProofError(
"Failed to initialize ZK prover",
{ error, config: this.config }
);
}
}
/**
* Generate a ZK proof for the given input data
*/
async generateProof(input) {
if (!this.isInitialized) {
throw new ZkProofError(
"Prover not initialized",
{ code: ZK_ERROR_CODES.PROVER_NOT_INITIALIZED }
);
}
if (input.length > this.config.maxInputSize) {
throw new ZkProofError(
`Input size ${input.length} exceeds maximum ${this.config.maxInputSize}`,
{ code: ZK_ERROR_CODES.INVALID_INPUT_SIZE }
);
}
const startTime = performance.now();
try {
const result = await this.callRustProver(input);
const generationTime = performance.now() - startTime;
if (generationTime > ZK_CONSTANTS.TARGET_PROOF_TIME_MS) {
console.warn(
`Proof generation time ${generationTime}ms exceeds target ${ZK_CONSTANTS.TARGET_PROOF_TIME_MS}ms`
);
}
if (this.config.enableBenchmarking) {
this.logPerformance("Proof generation", generationTime);
}
return {
hash: result.hash,
proof: result.proof,
generationTime
};
} catch (error) {
throw new ZkProofError(
"Proof generation failed",
{ error, inputSize: input.length }
);
}
}
/**
* Generate proofs for multiple inputs in batch
*/
async generateBatchProofs(batch) {
if (!this.isInitialized) {
throw new ZkProofError(
"Prover not initialized",
{ code: ZK_ERROR_CODES.PROVER_NOT_INITIALIZED }
);
}
const startTime = performance.now();
const results = [];
try {
if (batch.parallelization) {
const promises = batch.inputs.map(async (input, index) => {
const result = await this.generateProof(input);
return { index, ...result };
});
const parallelResults = await Promise.all(promises);
results.push(...parallelResults);
} else {
for (let i = 0; i < batch.inputs.length; i += 1) {
const result = await this.generateProof(batch.inputs[i]);
results.push({ index: i, ...result });
}
}
const totalTime = performance.now() - startTime;
const averageTime = totalTime / batch.inputs.length;
return {
batchId: batch.batchId,
results,
totalTime,
averageTime
};
} catch (error) {
throw new ZkProofError(
"Batch proof generation failed",
{ error, batchSize: batch.inputs.length }
);
}
}
/**
* Get prover performance metrics
*/
getMetrics() {
return {
setupTime: this.setupTime,
proofTime: 0,
// Would be calculated from actual measurements
verificationTime: 0,
proofSize: 0,
circuitSize: 2 ** this.config.circuitK
};
}
/**
* Update prover configuration
*/
updateConfig(newConfig) {
const updatedConfig = { ...this.config, ...newConfig };
this.config = ProverConfigSchema.parse(updatedConfig);
}
/**
* Check if prover is initialized
*/
isReady() {
return this.isInitialized;
}
/**
* Cleanup resources
*/
async cleanup() {
this.isInitialized = false;
this.setupTime = 0;
}
/**
* Private method to setup the proving system
*/
async setupProvingSystem() {
await this.delay(100);
}
/**
* Private method to call the Rust prover via FFI
*/
async callRustProver(input) {
const hashBuffer = sha256(input);
const hash = Array.from(hashBuffer);
const proof = new Uint8Array(1024);
crypto.getRandomValues(proof);
await this.delay(50);
return { hash, proof };
}
/**
* Utility method for simulating async operations
*/
async delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
/**
* Log performance metrics
*/
logPerformance(operation, timeMs) {
if (this.config.logLevel === "DEBUG" || this.config.logLevel === "INFO") {
console.log(`[ZkProver] ${operation}: ${timeMs.toFixed(2)}ms`);
}
}
};
var ZkVerifier = class {
config;
isInitialized = false;
constructor(config = {}) {
this.config = {
circuitK: 14,
useCachedKeys: true,
timeout: 3e4,
maxInputSize: 1024 * 1024,
enableBenchmarking: false,
logLevel: "INFO",
...config
};
}
/**
* Initialize the verifier
*/
async initialize() {
if (this.isInitialized) {
return;
}
try {
await this.setupVerificationSystem();
this.isInitialized = true;
} catch (error) {
throw new ZkProofError(
"Failed to initialize ZK verifier",
{ error, config: this.config }
);
}
}
/**
* Verify a ZK proof against the original input
*/
async verifyProof(input, proof, expectedHash) {
if (!this.isInitialized) {
throw new ZkProofError(
"Verifier not initialized",
{ code: ZK_ERROR_CODES.PROVER_NOT_INITIALIZED }
);
}
const startTime = performance.now();
try {
const computedHashBuffer = sha256(input);
const computedHash = Array.from(computedHashBuffer);
if (!this.arraysEqual(computedHash, expectedHash)) {
return {
status: "INVALID",
message: "Hash mismatch",
verificationTime: performance.now() - startTime
};
}
const isValidProof = await this.verifyZkProof(proof, expectedHash);
const verificationTime = performance.now() - startTime;
if (this.config.enableBenchmarking) {
this.logPerformance("Proof verification", verificationTime);
}
return {
status: isValidProof ? "VALID" : "INVALID",
verificationTime,
publicInputsHash: this.arrayToHex(expectedHash)
};
} catch (error) {
return {
status: "ERROR",
message: error instanceof Error ? error.message : "Unknown error",
verificationTime: performance.now() - startTime
};
}
}
/**
* Verify only the proof (without re-computing the hash)
*/
async verifyProofOnly(proof, publicInputs) {
if (!this.isInitialized) {
throw new ZkProofError(
"Verifier not initialized",
{ code: ZK_ERROR_CODES.PROVER_NOT_INITIALIZED }
);
}
const startTime = performance.now();
try {
const isValid = await this.verifyZkProof(proof, publicInputs);
const verificationTime = performance.now() - startTime;
if (this.config.enableBenchmarking) {
this.logPerformance("Proof-only verification", verificationTime);
}
return {
status: isValid ? "VALID" : "INVALID",
verificationTime,
publicInputsHash: this.arrayToHex(publicInputs)
};
} catch (error) {
return {
status: "ERROR",
message: error instanceof Error ? error.message : "Unknown error",
verificationTime: performance.now() - startTime
};
}
}
/**
* Batch verify multiple proofs
*/
async verifyBatchProofs(proofs) {
if (!this.isInitialized) {
throw new ZkProofError(
"Verifier not initialized",
{ code: ZK_ERROR_CODES.PROVER_NOT_INITIALIZED }
);
}
const promises = proofs.map(
({ input, proof, expectedHash }) => this.verifyProof(input, proof, expectedHash)
);
return Promise.all(promises);
}
/**
* Check if verifier is ready
*/
isReady() {
return this.isInitialized;
}
/**
* Cleanup resources
*/
async cleanup() {
this.isInitialized = false;
}
/**
* Private method to setup verification system
*/
async setupVerificationSystem() {
await this.delay(50);
}
/**
* Private method to verify the ZK proof
*/
async verifyZkProof(proof, publicInputs) {
await this.delay(10);
return proof.length > 0 && publicInputs.length === 32;
}
/**
* Utility method to compare arrays
*/
arraysEqual(a, b) {
if (a.length !== b.length) return false;
return a.every((val, index) => val === b[index]);
}
/**
* Convert number array to hex string
*/
arrayToHex(arr) {
return `0x${arr.map((b) => b.toString(16).padStart(2, "0")).join("")}`;
}
/**
* Utility method for simulating async operations
*/
async delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
/**
* Log performance metrics
*/
logPerformance(operation, timeMs) {
if (this.config.logLevel === "DEBUG" || this.config.logLevel === "INFO") {
console.log(`[ZkVerifier] ${operation}: ${timeMs.toFixed(2)}ms`);
}
}
};
// src/zk/client.ts
var ZkClient = class {
prover;
verifier;
isInitialized = false;
constructor(config = {}) {
this.prover = new ZkProver(config);
this.verifier = new ZkVerifier(config);
}
/**
* Initialize both prover and verifier
*/
async initialize() {
if (this.isInitialized) {
return;
}
try {
await Promise.all([
this.prover.initialize(),
this.verifier.initialize()
]);
this.isInitialized = true;
} catch (error) {
throw new ZkProofError(
"Failed to initialize ZK client",
{ error }
);
}
}
/**
* Generate a proof for input data
*/
async generateProof(input) {
this.ensureInitialized();
return this.prover.generateProof(input);
}
/**
* Verify a proof against the original input
*/
async verifyProof(input, proof, expectedHash) {
this.ensureInitialized();
return this.verifier.verifyProof(input, proof, expectedHash);
}
/**
* Generate and verify a proof in one operation
*/
async proveAndVerify(input) {
this.ensureInitialized();
const proof = await this.generateProof(input);
const verification = await this.verifyProof(
input,
proof.proof,
proof.hash
);
return { proof, verification };
}
/**
* Generate proofs for multiple inputs
*/
async generateBatchProofs(batch) {
this.ensureInitialized();
return this.prover.generateBatchProofs(batch);
}
/**
* Verify multiple proofs
*/
async verifyBatchProofs(proofs) {
this.ensureInitialized();
return this.verifier.verifyBatchProofs(proofs);
}
/**
* Get performance metrics from the prover
*/
getMetrics() {
this.ensureInitialized();
return this.prover.getMetrics();
}
/**
* Check if the client is ready
*/
isReady() {
return this.isInitialized && this.prover.isReady() && this.verifier.isReady();
}
/**
* Cleanup resources
*/
async cleanup() {
await Promise.all([
this.prover.cleanup(),
this.verifier.cleanup()
]);
this.isInitialized = false;
}
/**
* Update configuration for both prover and verifier
*/
updateConfig(newConfig) {
this.prover.updateConfig(newConfig);
}
/**
* Ensure the client is initialized
*/
ensureInitialized() {
if (!this.isInitialized) {
throw new ZkProofError(
"ZK client not initialized. Call initialize() first.",
{ code: ZK_ERROR_CODES.PROVER_NOT_INITIALIZED }
);
}
}
};
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 = z.object({
/** Contract bytecode */
bytecode: z.string().regex(/^0x[a-fA-F0-9]*$/),
/** Constructor arguments */
constructorArgs: z.array(z.unknown()).default([]),
/** Gas limit for deployment */
gasLimit: z.bigint().optional(),
/** Gas price */
gasPrice: z.bigint().optional(),
/** Salt for CREATE2 deployment */
salt: z.string().regex(/^0x[a-fA-F0-9]{64}$/).optional()
});
var ContractCallConfigSchema = z.object({
/** Contract address */
address: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Function name */
functionName: z.string().min(1),
/** Function arguments */
args: z.array(z.unknown()).default([]),
/** Value to send with the call */
value: z.bigint().default(0n),
/** Gas limit */
gasLimit: z.bigint().optional()
});
var AccountOperationResultSchema = z.object({
/** Transaction hash */
txHash: z.string().regex(/^0x[a-fA-F0-9]{64}$/),
/** Block number */
blockNumber: z.number().int().nonnegative(),
/** Gas used */
gasUsed: z.bigint(),
/** Operation status */
status: z.enum(["SUCCESS", "FAILED", "PENDING"]),
/** Error message if failed */
error: z.string().optional()
});
var SignatureDataSchema = z.object({
/** Signer address */
signer: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Signature bytes */
signature: z.string().regex(/^0x[a-fA-F0-9]*$/),
/** Signature type (ECDSA, etc.) */
signatureType: z.enum(["ECDSA", "EIP1271"]).default("ECDSA")
});
var GasEstimationSchema = z.object({
/** Estimated gas limit */
gasLimit: z.bigint(),
/** Estimated gas price */
gasPrice: z.bigint(),
/** Maximum fee per gas (EIP-1559) */
maxFeePerGas: z.bigint().optional(),
/** Maximum priority fee per gas (EIP-1559) */
maxPriorityFeePerGas: z.bigint().optional(),
/** Estimated total cost in wei */
totalCost: z.bigint()
});
var EventFilterSchema = z.object({
/** Contract address */
address: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Event topics */
topics: z.array(z.string().regex(/^0x[a-fA-F0-9]{64}$/)).optional(),
/** From block */
fromBlock: z.union([z.number().int().nonnegative(), z.literal("latest")]).default("latest"),
/** To block */
toBlock: z.union([z.number().int().nonnegative(), z.literal("latest")]).default("latest")
});
var ContractEventSchema = z.object({
/** Event name */
eventName: z.string(),
/** Contract address */
address: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
/** Block number */
blockNumber: z.number().int().nonnegative(),
/** Transaction hash */
transactionHash: z.string().regex(/^0x[a-fA-F0-9]{64}$/),
/** Event data */
data: z.record(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.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.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.keccak256(
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.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.keccak256(
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.getBytes(hash)
);
const paymasterData = 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.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.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.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.solidityPacked(
["address", "uint256", "bytes32", "uint48", "uint48"],
[
userOp.sender,
userOp.nonce,
ethers.keccak256(userOp.callData),
validUntil,
validAfter
]
);
return 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.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.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.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.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.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.Contract(
this.address,