UNPKG

@owlprotocol/contracts-account-abstraction

Version:

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

187 lines (166 loc) 6.88 kB
import { describe, test, beforeAll, beforeEach, expect } from "vitest"; import { Address, Chain, Transport, createPublicClient, createWalletClient, http, parseEther, PrivateKeyAccount, padHex, LocalAccount, custom, Hex, EIP1193RequestFn, zeroHash, nonceManager, } from "viem"; import { localhost } from "viem/chains"; import { SmartAccount, waitForUserOperationReceipt, createBundlerClient, BundlerClient, } from "viem/account-abstraction"; import { getLocalAccount, getDeployDeterministicAddress, getDeployDeterministicFunctionData, } from "@owlprotocol/viem-utils"; import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { SimpleSmartAccountImplementation, toSimpleSmartAccount } from "permissionless/accounts"; import { createSmartAccountClient, SmartAccountClient } 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, }); // Contracts const entryPointAddress = erc4337Contracts.entrypoint; const entryPointSimulationsAddress = erc4337Contracts.pimlicoEntrypointSimulations; const factoryAddress = erc4337Contracts.simpleAccountFactory; // AA clients let bundlerRequest: EIP1193RequestFn; let bundlerTransport: Transport; let bundlerClient: BundlerClient; beforeAll(async () => { // AA Clients const requestOverride = createPublicEIP1193(createHttpEIP1193(`http://127.0.0.1:${port}`)); bundlerRequest = createBackendBundlerEIP1193( createBackendBundler({ account: getLocalAccount(0) as LocalAccount, 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", () => { // Generated account on each test let owner: PrivateKeyAccount; let smartAccount: SmartAccount<SimpleSmartAccountImplementation<"0.7">>; let smartAccountClient: SmartAccountClient<Transport, Chain, SmartAccount<SimpleSmartAccountImplementation>>; let contractAddressExpected: Address; let contractDeployTransaction: { to: Address; data: Hex }; 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: owner, factoryAddress, entryPoint: { address: entryPointAddress, version: "0.7", }, }); smartAccountClient = createSmartAccountClient({ account: smartAccount, chain: publicClient.chain, bundlerTransport, }); //Deploy hello world contract const deployParams = { // "random" salt salt: padHex(owner.address, { size: 32 }), bytecode: MyContract.bytecode, }; contractDeployTransaction = getDeployDeterministicFunctionData(deployParams); contractAddressExpected = getDeployDeterministicAddress(deployParams); //Pre-fund wallet just to pay tx cost 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 as any); 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(); }); }); });