UNPKG

@owlprotocol/contracts-account-abstraction

Version:

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

198 lines (197 loc) 7.02 kB
import { describe, test, beforeAll, beforeEach, expect } from "vitest"; import { createPublicClient, createWalletClient, http, parseEther, padHex, custom, numberToHex, nonceManager } from "viem"; import { localhost } from "viem/chains"; import { sepolia } from "@owlprotocol/chains"; import { entryPoint07Address, createPaymasterClient, waitForUserOperationReceipt, createBundlerClient } from "viem/account-abstraction"; import { getLocalAccount, getDeployDeterministicAddress, getDeployDeterministicFunctionData, getUtilityAccount, getPaymasterSignerAccount } from "@owlprotocol/viem-utils"; import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { toSimpleSmartAccount } from "permissionless/accounts"; import { createSmartAccountClient } from "permissionless/clients"; import { getUserOperationGasPrice } from "permissionless/actions/pimlico"; import { createHttpEIP1193, createPublicEIP1193 } from "@owlprotocol/backend-public/eip1193"; import { NODE_ENV } from "@owlprotocol/envvars"; import { createBackendBundlerEIP1193 } from "./createBundlerEIP1193.js"; import { createBackendPaymasterEIP1193 } from "./createPaymasterEIP1193.js"; import { createBackendBundler } from "../clients/createBackendBundler.js"; import { createBackendPaymaster } from "../clients/createBackendPaymaster.js"; import { port } from "../test/constants.js"; import { getSimpleAccountAddress } from "../SimpleAccount.js"; import { ERC1967Proxy } from "../artifacts/ERC1967Proxy.js"; import { MyContract } from "../artifacts/MyContract.js"; import { erc4337Contracts, setupVerifyingPaymaster, topupPaymaster } from "../setupERC4337Contracts.js"; describe("eip1993/createPaymasterEIP1193.test.ts", function() { let chain; let account; let transport; if (NODE_ENV === "test") { chain = { ...localhost, rpcUrls: { default: { http: [`http://127.0.0.1:${port}`] } } }; account = getLocalAccount(0, { nonceManager }); transport = http(chain.rpcUrls.default.http[0]); } else { chain = sepolia; account = getUtilityAccount({ nonceManager }); transport = http(sepolia.rpcUrls.drpc.http[0]); } const publicClient = createPublicClient({ chain, transport }); const walletClient = createWalletClient({ account, chain, transport }); const entryPointAddress = erc4337Contracts.entrypoint; const entryPointSimulationsAddress = erc4337Contracts.pimlicoEntrypointSimulations; const factoryAddress = erc4337Contracts.simpleAccountFactory; let paymasterAddress; let bundlerTransport; let bundlerClient; let paymasterRequest; let paymasterTransport; let paymasterClient; beforeAll(async () => { paymasterAddress = (await setupVerifyingPaymaster(walletClient, { verifyingSignerAddress: getPaymasterSignerAccount({ nonceManager }).address })).address; if (NODE_ENV === "test") { const { hash: topupHash } = await topupPaymaster(walletClient, { paymaster: paymasterAddress, minBalance: parseEther("1") }); if (topupHash) { await publicClient.waitForTransactionReceipt({ hash: topupHash }); } } const requestOverride = createPublicEIP1193(createHttpEIP1193(`http://127.0.0.1:${port}`)); const bundlerRequest = createBackendBundlerEIP1193( createBackendBundler({ account: walletClient.account, chain, transport, entryPointSimulationsAddress }), //viem middleware bypass { requestOverride } ); bundlerTransport = custom({ request: bundlerRequest, config: { retryCount: 0 } }); bundlerClient = createBundlerClient({ transport: bundlerTransport }); paymasterRequest = createBackendPaymasterEIP1193( createBackendPaymaster({ account: getPaymasterSignerAccount({ nonceManager }), chain, transport, paymaster: paymasterAddress }) ); paymasterTransport = custom({ request: paymasterRequest, config: { retryCount: 0 } }); paymasterClient = createPaymasterClient({ transport: paymasterTransport }); }); describe("paymasterClient", () => { test("pm_getPaymasterStubData", async () => { const result = await paymasterClient.request({ method: "pm_getPaymasterStubData", params: [{}, entryPoint07Address, numberToHex(chain.id), {}] }); expect(result).toBeDefined(); }); }); describe("smartAccountClient", () => { let owner; let smartAccount; let smartAccountClient; let contractAddressExpected; let contractDeployTransaction; beforeEach(async () => { owner = privateKeyToAccount(generatePrivateKey()); const smartAccountAddress = getSimpleAccountAddress( { owner: owner.address, salt: 0n }, { factoryAddress, proxyBytecode: ERC1967Proxy.bytecode } ); smartAccount = await toSimpleSmartAccount({ address: smartAccountAddress, client: publicClient, owner, factoryAddress, entryPoint: { address: entryPointAddress, version: "0.7" } }); smartAccountClient = createSmartAccountClient({ account: smartAccount, chain: publicClient.chain, paymaster: paymasterClient, bundlerTransport, userOperation: { estimateFeesPerGas: async () => (await getUserOperationGasPrice(paymasterClient)).fast } }); const deployParams = { // "random" salt salt: padHex(owner.address, { size: 32 }), bytecode: MyContract.bytecode }; contractDeployTransaction = getDeployDeterministicFunctionData(deployParams); contractAddressExpected = getDeployDeterministicAddress(deployParams); }); test("smartAccountClient.sendTransaction", async () => { const hash = await smartAccountClient.sendTransaction(contractDeployTransaction); const receipt = await publicClient.waitForTransactionReceipt({ hash }); expect(receipt).toBeDefined(); const contractBytecode = await publicClient.getCode({ address: contractAddressExpected }); expect(contractBytecode).toBeDefined(); }); test("smartAccountClient.prepareUserOperation", async () => { const userOperation = await smartAccountClient.prepareUserOperation({ calls: [contractDeployTransaction] }); userOperation.signature = await smartAccount.signUserOperation(userOperation); const userOpHash = await bundlerClient.sendUserOperation(userOperation); const userOpReceipt = await waitForUserOperationReceipt(bundlerClient, { hash: userOpHash }); expect(userOpReceipt).toBeDefined(); console.log({ contractAddressExpected }); const contractBytecode = await publicClient.getCode({ address: contractAddressExpected }); expect(contractBytecode).toBeDefined(); }); }); });