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