UNPKG

@owlprotocol/contracts-account-abstraction

Version:

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

152 lines (151 loc) 5.51 kB
import { describe, test, beforeAll, beforeEach, expect } from "vitest"; import { createPublicClient, createWalletClient, http, parseEther, padHex, custom, zeroHash, nonceManager } from "viem"; import { localhost } from "viem/chains"; import { waitForUserOperationReceipt, createBundlerClient } from "viem/account-abstraction"; import { getLocalAccount, getDeployDeterministicAddress, getDeployDeterministicFunctionData } from "@owlprotocol/viem-utils"; import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { toSimpleSmartAccount } from "permissionless/accounts"; import { createSmartAccountClient } from "permissionless/clients"; import { createHttpEIP1193, createPublicEIP1193 } from "@owlprotocol/backend-public/eip1193"; import { createBackendBundlerEIP1193 } from "./createBundlerEIP1193.js"; import { createBackendBundler } from "../clients/createBackendBundler.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 } from "../setupERC4337Contracts.js"; describe("eip1993/createBundlerEIP1193.test.ts", function() { const chain = { ...localhost, rpcUrls: { default: { http: [`http://127.0.0.1:${port}`] } } }; const transport = http(chain.rpcUrls.default.http[0]); const publicClient = createPublicClient({ chain, transport }); const walletClient = createWalletClient({ account: getLocalAccount(0, { nonceManager }), chain, transport }); const entryPointAddress = erc4337Contracts.entrypoint; const entryPointSimulationsAddress = erc4337Contracts.pimlicoEntrypointSimulations; const factoryAddress = erc4337Contracts.simpleAccountFactory; let bundlerRequest; let bundlerTransport; let bundlerClient; beforeAll(async () => { const requestOverride = createPublicEIP1193(createHttpEIP1193(`http://127.0.0.1:${port}`)); bundlerRequest = createBackendBundlerEIP1193( createBackendBundler({ account: getLocalAccount(0), chain: localhost, transport, entryPointSimulationsAddress }), //viem middleware bypass { requestOverride } ); bundlerTransport = custom({ request: bundlerRequest, config: { retryCount: 0 } }); bundlerClient = createBundlerClient({ transport: bundlerTransport }); }); describe("bundlerClient", () => { test("eth_getUserOperationByHash", async () => { const result = await bundlerClient.request({ method: "eth_getUserOperationByHash", params: [zeroHash] }); expect(result).toBeNull(); }); test("eth_getUserOperationReceipt", async () => { const result = await bundlerClient.request({ method: "eth_getUserOperationReceipt", params: [zeroHash] }); expect(result).toBeNull(); }); }); 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, bundlerTransport }); const deployParams = { // "random" salt salt: padHex(owner.address, { size: 32 }), bytecode: MyContract.bytecode }; contractDeployTransaction = getDeployDeterministicFunctionData(deployParams); contractAddressExpected = getDeployDeterministicAddress(deployParams); const fundSimpleAccountHash = await walletClient.sendTransaction({ to: smartAccount.address, value: parseEther("5") }); await publicClient.waitForTransactionReceipt({ hash: fundSimpleAccountHash }); }); 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(); const contractBytecode = await publicClient.getCode({ address: contractAddressExpected }); expect(contractBytecode).toBeDefined(); }); }); });