UNPKG

@owlprotocol/contracts-account-abstraction

Version:

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

244 lines (217 loc) 8.77 kB
import { describe, test, beforeAll, beforeEach, expect } from "vitest"; import { Address, Chain, Transport, createPublicClient, createWalletClient, http, parseEther, PrivateKeyAccount, padHex, custom, Hex, EIP1193RequestFn, numberToHex, nonceManager, Account, HttpTransport, } from "viem"; import { localhost } from "viem/chains"; import { sepolia } from "@owlprotocol/chains"; import { entryPoint07Address, createPaymasterClient, SmartAccount, waitForUserOperationReceipt, PaymasterClient, createBundlerClient, BundlerClient, } from "viem/account-abstraction"; import { getLocalAccount, getDeployDeterministicAddress, getDeployDeterministicFunctionData, getUtilityAccount, getPaymasterSignerAccount, } from "@owlprotocol/viem-utils"; import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { SimpleSmartAccountImplementation, toSimpleSmartAccount } from "permissionless/accounts"; import { createSmartAccountClient, SmartAccountClient } 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: Chain; let account: Account; let transport: HttpTransport; 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 as unknown as Chain; account = getUtilityAccount({ nonceManager }); transport = http(sepolia.rpcUrls.drpc!.http[0]); } const publicClient = createPublicClient({ chain, transport, }); const walletClient = createWalletClient({ account, chain, transport, }); // Contracts const entryPointAddress = erc4337Contracts.entrypoint; const entryPointSimulationsAddress = erc4337Contracts.pimlicoEntrypointSimulations; const factoryAddress = erc4337Contracts.simpleAccountFactory; let paymasterAddress: Address; // AA clients let bundlerTransport: Transport; let bundlerClient: BundlerClient; // eslint-disable-next-line @typescript-eslint/no-unused-vars let paymasterRequest: EIP1193RequestFn; let paymasterTransport: Transport; let paymasterClient: PaymasterClient; beforeAll(async () => { // Paymaster 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 }); } } // AA Clients 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: [{} as any, entryPoint07Address, numberToHex(chain.id), {}], }); expect(result).toBeDefined(); }); }); describe("smartAccountClient", () => { // Generated account on each test let owner: PrivateKeyAccount; let smartAccount: SmartAccount<SimpleSmartAccountImplementation<"0.7">>; let smartAccountClient: SmartAccountClient<Transport, Chain, SmartAccount>; 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, paymaster: paymasterClient, bundlerTransport, userOperation: { estimateFeesPerGas: async () => (await getUserOperationGasPrice(paymasterClient)).fast, }, }); //Deploy hello world contract 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 as any); const userOpHash = await bundlerClient.sendUserOperation(userOperation as any); const userOpReceipt = await waitForUserOperationReceipt(bundlerClient, { hash: userOpHash }); expect(userOpReceipt).toBeDefined(); console.log({ contractAddressExpected }); const contractBytecode = await publicClient.getCode({ address: contractAddressExpected }); expect(contractBytecode).toBeDefined(); }); }); });