@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
JavaScript
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();
});
});
});