UNPKG

@owlprotocol/contracts-account-abstraction

Version:

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

198 lines (197 loc) 8.73 kB
var import_vitest = require("vitest"); var import_viem = require("viem"); var import_chains = require("viem/chains"); var import_viem_utils = require("@owlprotocol/viem-utils"); var import_accounts = require("viem/accounts"); var import_account_abstraction = require("viem/account-abstraction"); var import_constants = require("./test/constants.js"); var import_SimpleAccountFactory = require("./artifacts/SimpleAccountFactory.js"); var import_SimpleAccount = require("./SimpleAccount.js"); var import_constants2 = require("./constants.js"); var import_ERC1967Proxy = require("./artifacts/ERC1967Proxy.js"); var import_SimpleAccount2 = require("./artifacts/SimpleAccount.js"); var import_UserOperation = require("./models/UserOperation.js"); var import_IEntryPoint = require("./artifacts/IEntryPoint.js"); var import_setupERC4337Contracts = require("./setupERC4337Contracts.js"); var import_PackedUserOperation = require("./models/PackedUserOperation.js"); var import_estimateUserOperationGas = require("./actions/bundler/estimateUserOperationGas.js"); (0, import_vitest.describe)("SimpleAccount.test.ts", function() { const chain = { ...import_chains.localhost, rpcUrls: { default: { http: [`http://127.0.0.1:${import_constants.port}`] } } }; const transport = (0, import_viem.http)(chain.rpcUrls.default.http[0]); const publicClient = (0, import_viem.createPublicClient)({ chain, transport }); const walletClient = (0, import_viem.createWalletClient)({ account: (0, import_viem_utils.getLocalAccount)(0, { nonceManager: import_viem.nonceManager }), chain, transport }); let account; const entryPoint = import_setupERC4337Contracts.erc4337Contracts.entrypoint; const simpleAccountFactory = import_setupERC4337Contracts.erc4337Contracts.simpleAccountFactory; (0, import_vitest.beforeEach)(async () => { account = (0, import_accounts.privateKeyToAccount)((0, import_accounts.generatePrivateKey)()); }); (0, import_vitest.describe)("Deploy Simple Account", () => { (0, import_vitest.test)("getSimpleAccountAddress", async () => { const simpleAccountImplementation = await publicClient.readContract({ address: simpleAccountFactory, abi: import_SimpleAccountFactory.SimpleAccountFactory.abi, functionName: "accountImplementation" }); (0, import_vitest.expect)(simpleAccountImplementation).toBe(import_constants2.SIMPLE_ACCOUNT_IMPLEMENTATION_ADDRESS); const simpleAccountAddressFromFactory = await publicClient.readContract({ address: simpleAccountFactory, abi: import_SimpleAccountFactory.SimpleAccountFactory.abi, functionName: "getAddress", args: [account.address, 0n] }); const simpleAccountAddressOffchain = (0, import_SimpleAccount.getSimpleAccountAddress)( { owner: account.address, salt: 0n }, { factoryAddress: simpleAccountFactory, proxyBytecode: import_ERC1967Proxy.ERC1967Proxy.bytecode } ); (0, import_vitest.expect)(simpleAccountAddressOffchain).toBe(simpleAccountAddressFromFactory); const accountExistingBytecode = await publicClient.getCode({ address: simpleAccountAddressOffchain }); (0, import_vitest.expect)( accountExistingBytecode, "Generated smart account counterfactual address should have 0x code" ).toBeUndefined(); const { request: createAccountRequest } = await publicClient.simulateContract({ account: walletClient.account, address: simpleAccountFactory, abi: import_SimpleAccountFactory.SimpleAccountFactory.abi, functionName: "createAccount", args: [account.address, 0n] }); const createAccountHash = await walletClient.writeContract(createAccountRequest); await publicClient.waitForTransactionReceipt({ hash: createAccountHash }); const accountBytecode = await publicClient.getCode({ address: simpleAccountAddressOffchain }); (0, import_vitest.expect)(accountBytecode).toBeDefined(); const accountOwner = await publicClient.readContract({ address: simpleAccountAddressOffchain, abi: import_SimpleAccount2.SimpleAccount.abi, functionName: "owner" }); (0, import_vitest.expect)(accountOwner).toBe(account.address); }); }); (0, import_vitest.describe)("Exec existing Simple Account", () => { let simpleAccount; (0, import_vitest.beforeEach)(async () => { const simpleAccountAddress = (0, import_SimpleAccount.getSimpleAccountAddress)( { owner: account.address, salt: 0n }, { factoryAddress: simpleAccountFactory, proxyBytecode: import_ERC1967Proxy.ERC1967Proxy.bytecode } ); const simpleAccountFactoryData = (0, import_viem.encodeFunctionData)({ abi: import_SimpleAccountFactory.SimpleAccountFactory.abi, functionName: "createAccount", args: [account.address, 0n] }); simpleAccount = { address: simpleAccountAddress, factoryData: simpleAccountFactoryData, factoryAddress: simpleAccountFactory }; const { request: createAccountRequest } = await publicClient.simulateContract({ account: walletClient.account, address: simpleAccountFactory, abi: import_SimpleAccountFactory.SimpleAccountFactory.abi, functionName: "createAccount", args: [account.address, 0n] }); const createAccountHash = await walletClient.writeContract(createAccountRequest); await publicClient.waitForTransactionReceipt({ hash: createAccountHash }); const fundSimpleAccountHash = await walletClient.sendTransaction({ to: simpleAccount.address, value: (0, import_viem.parseEther)("1") }); await publicClient.waitForTransactionReceipt({ hash: fundSimpleAccountHash }); }); (0, import_vitest.test)("submitUserOp - manual", async () => { const to = (0, import_accounts.privateKeyToAccount)((0, import_accounts.generatePrivateKey)()).address; const value = 1n; const data = "0x"; const callData = (0, import_viem.encodeFunctionData)({ abi: [ { inputs: [ { name: "dest", type: "address" }, { name: "value", type: "uint256" }, { name: "func", type: "bytes" } ], name: "execute", outputs: [], stateMutability: "nonpayable", type: "function" } ], args: [to, value, data] }); const gasPrice = await publicClient.estimateFeesPerGas(); const userOpData = { sender: simpleAccount.address, nonce: 0n, callData, maxFeePerGas: gasPrice.maxFeePerGas }; const { preVerificationGas, verificationGasLimit, callGasLimit } = await (0, import_estimateUserOperationGas.estimateUserOperationGas)( { ...publicClient, entryPointSimulationsAddress: import_setupERC4337Contracts.erc4337Contracts.pimlicoEntrypointSimulations }, userOpData ); const userOp = { sender: simpleAccount.address, nonce: 0n, signature: "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c", callData, callGasLimit, verificationGasLimit, preVerificationGas, maxFeePerGas: gasPrice.maxFeePerGas, maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas }; const userOpHash = (0, import_account_abstraction.getUserOperationHash)({ userOperation: userOp, entryPointAddress: entryPoint, entryPointVersion: "0.7", chainId: import_chains.localhost.id }); const signature = await account.signMessage({ message: { raw: userOpHash } }); userOp.signature = signature; const userOpPacked = (0, import_PackedUserOperation.toPackedUserOperation)((0, import_UserOperation.encodeUserOp)(userOp)); const handleOpsArgs = [[userOpPacked], walletClient.account.address]; const { request } = await publicClient.simulateContract({ account: walletClient.account, address: entryPoint, abi: import_IEntryPoint.IEntryPoint.abi, functionName: "handleOps", args: handleOpsArgs }); const handleOpsHash = await walletClient.writeContract(request); await publicClient.waitForTransactionReceipt({ hash: handleOpsHash }); const balance = await publicClient.getBalance({ address: to }); (0, import_vitest.expect)(balance).toBe(value); }); }); });