UNPKG

@owlprotocol/contracts-account-abstraction

Version:

ERC4337 Account Abstraction support for deploying relevant smart contracts and interacting with UserOps.

230 lines (229 loc) 7.48 kB
import { getOrDeployDeterministicContract, DETERMINISTIC_DEPLOYER_ADDRESS, getDeployDeterministicAddress, getOrPrepareDeterministicContract } from "@owlprotocol/viem-utils"; import { encodeDeployData, formatEther, zeroHash } from "viem"; import { entryPoint07Address } from "viem/account-abstraction"; import { getCode, readContract, sendTransaction, simulateContract, waitForTransactionReceipt, writeContract } from "viem/actions"; import { getAction } from "viem/utils"; import { ENTRYPOINT_SALT_V07 } from "./constants.js"; import { EntryPoint } from "./artifacts/EntryPoint.js"; import { SimpleAccountFactory } from "./artifacts/SimpleAccountFactory.js"; import { VerifyingPaymaster } from "./artifacts/VerifyingPaymaster.js"; import { EntryPointSimulations } from "./artifacts/EntryPointSimulations.js"; import { PimlicoEntryPointSimulations } from "./artifacts/PimlicoEntryPointSimulations.js"; function getERC4337Contracts() { const deterministicDeployer = DETERMINISTIC_DEPLOYER_ADDRESS; const entrypoint = getDeployDeterministicAddress({ salt: ENTRYPOINT_SALT_V07, bytecode: EntryPoint.bytecode }); const simpleAccountFactory = getDeployDeterministicAddress({ salt: zeroHash, bytecode: encodeDeployData({ abi: SimpleAccountFactory.abi, bytecode: SimpleAccountFactory.bytecode, args: [entrypoint] }) }); const entrypointSimulations = getDeployDeterministicAddress({ salt: zeroHash, bytecode: encodeDeployData({ abi: EntryPointSimulations.abi, bytecode: EntryPointSimulations.bytecode, args: [] }) }); const pimlicoEntrypointSimulations = getDeployDeterministicAddress({ salt: zeroHash, bytecode: encodeDeployData({ abi: PimlicoEntryPointSimulations.abi, bytecode: PimlicoEntryPointSimulations.bytecode, args: [entrypointSimulations] }) }); return { deterministicDeployer, entrypoint, simpleAccountFactory, entrypointSimulations, pimlicoEntrypointSimulations }; } const erc4337Contracts = getERC4337Contracts(); async function prepareERC4337Contracts(client) { const requests = []; const [entrypoint, simpleAccountFactory, entrypointSimulations, pimlicoEntrypointSimulations] = await Promise.all([ getOrPrepareDeterministicContract( client, //Extracted salt (first 32 bytes) from original tx //https://etherscan.io/tx/0x5c81ea86f6c54481d3e21c78675b4f1d985c1fa62b678dcdfdf7934ddd6e127e { salt: ENTRYPOINT_SALT_V07, bytecode: EntryPoint.bytecode } ), getOrPrepareDeterministicContract(client, { salt: zeroHash, bytecode: encodeDeployData({ abi: SimpleAccountFactory.abi, bytecode: SimpleAccountFactory.bytecode, args: [erc4337Contracts.entrypoint] }) }), getOrPrepareDeterministicContract(client, { salt: zeroHash, bytecode: encodeDeployData({ abi: EntryPointSimulations.abi, bytecode: EntryPointSimulations.bytecode, args: [] }) }), getOrPrepareDeterministicContract(client, { salt: zeroHash, bytecode: encodeDeployData({ abi: PimlicoEntryPointSimulations.abi, bytecode: PimlicoEntryPointSimulations.bytecode, args: [erc4337Contracts.entrypointSimulations] }) }) ]); if (entrypoint.address != entryPoint07Address) { throw new Error( `Entrypoint v0.7 deployed address ${entryPoint07Address} (expected) != ${entrypoint.address} (actual)` ); } if (entrypoint.request) requests.push(entrypoint.request); if (simpleAccountFactory.request) requests.push(simpleAccountFactory.request); if (entrypointSimulations.request) requests.push(entrypointSimulations.request); if (pimlicoEntrypointSimulations.request) requests.push(pimlicoEntrypointSimulations.request); return { requests, entrypoint, simpleAccountFactory, entrypointSimulations, pimlicoEntrypointSimulations }; } async function setupERC4337Contracts(client) { if (!client.account.nonceManager) { throw new Error("client.account.nonceManager undefined"); } const erc4337Contracts2 = await prepareERC4337Contracts(client); const transactions = await Promise.all( erc4337Contracts2.requests.map( (request) => getAction(client, sendTransaction, "sendTransaction")(request) ) ); const receipts = await Promise.all( transactions.map((hash) => getAction(client, waitForTransactionReceipt, "waitForTransactionReceipt")({ hash })) ); return { ...erc4337Contracts2, transactions, receipts }; } function getVerifyingPaymaster(params) { const { verifyingSignerAddress } = params; return getDeployDeterministicAddress({ salt: zeroHash, bytecode: encodeDeployData({ abi: VerifyingPaymaster.abi, bytecode: VerifyingPaymaster.bytecode, args: [entryPoint07Address, verifyingSignerAddress] }) }); } async function setupVerifyingPaymaster(client, parameters) { const { verifyingSignerAddress } = parameters; const entrypointBytecode = await getAction(client, getCode, "getCode")({ address: entryPoint07Address }); if (!entrypointBytecode) { throw new Error( `EntryPoint v0.7 ${entryPoint07Address} not deployed. Consider deploying with setupERC4337Contracts()` ); } const verifyingPaymaster = await getOrDeployDeterministicContract(client, { salt: zeroHash, bytecode: encodeDeployData({ abi: VerifyingPaymaster.abi, bytecode: VerifyingPaymaster.bytecode, args: [entryPoint07Address, verifyingSignerAddress] }) }); if (verifyingPaymaster.hash) { await getAction( client, waitForTransactionReceipt, "waitForTransactionReceipt" )({ hash: verifyingPaymaster.hash }); } return verifyingPaymaster; } async function topupPaymaster(client, parameters) { const { paymaster, minBalance } = parameters; if (minBalance == 0n && parameters.targetBalance === void 0) { throw new Error(`topupAddressL2: minBalance 0, targetBalance MUST be defined`); } const targetBalance = parameters.targetBalance ?? minBalance; if (minBalance > targetBalance) { throw new Error( `topupPaymaster: minBalance (${formatEther(minBalance)}) > targetBalance (${formatEther(targetBalance)})` ); } if (0n >= targetBalance) { throw new Error(`topupPaymaster: 0 >= targetBalance (${formatEther(targetBalance)})`); } const balance = await getAction( client, readContract, "readContract" )({ address: entryPoint07Address, abi: EntryPoint.abi, functionName: "balanceOf", args: [paymaster] }); const targetDeficit = targetBalance - balance; if (targetDeficit > 0n && (balance < minBalance || minBalance == 0n)) { const paymasterDeposit = await getAction( client, simulateContract, "simulateContract" )({ account: client.account, chain: client.chain, address: paymaster, abi: VerifyingPaymaster.abi, functionName: "deposit", value: targetDeficit, args: [] }); const paymasterDepositHash = await getAction(client, writeContract, "writeContract")(paymasterDeposit.request); return { balance, hash: paymasterDepositHash }; } return { balance }; } export { erc4337Contracts, getERC4337Contracts, getVerifyingPaymaster, prepareERC4337Contracts, setupERC4337Contracts, setupVerifyingPaymaster, topupPaymaster };