@owlprotocol/contracts-account-abstraction
Version:
ERC4337 Account Abstraction support for deploying relevant smart contracts and interacting with UserOps.
245 lines (244 loc) • 10.5 kB
JavaScript
var import_vitest = require("vitest");
var import_viem = require("viem");
var import_chains = require("viem/chains");
var import_viem_utils = require("@owlprotocol/viem-utils");
var import_accounts = require("viem/accounts");
var import_account_abstraction = require("viem/account-abstraction");
var import_constants = require("./test/constants.js");
var import_VerifyingPaymaster = require("./artifacts/VerifyingPaymaster.js");
var import_IEntryPoint = require("./artifacts/IEntryPoint.js");
var import_SimpleAccount = require("./SimpleAccount.js");
var import_ERC1967Proxy = require("./artifacts/ERC1967Proxy.js");
var import_SimpleAccountFactory = require("./artifacts/SimpleAccountFactory.js");
var import_UserOperation = require("./models/UserOperation.js");
var import_setupERC4337Contracts = require("./setupERC4337Contracts.js");
var import_PackedUserOperation = require("./models/PackedUserOperation.js");
var import_VerifyingPaymaster2 = require("./VerifyingPaymaster.js");
var import_estimateUserOperationGas = require("./actions/bundler/estimateUserOperationGas.js");
(0, import_vitest.describe)("VerifyingPaymaster.test.ts", function() {
const chain = {
...import_chains.localhost,
rpcUrls: {
default: {
http: [`http://127.0.0.1:${import_constants.port}`]
}
}
};
const transport = (0, import_viem.http)(chain.rpcUrls.default.http[0]);
const publicClient = (0, import_viem.createPublicClient)({
chain,
transport
});
const walletClient = (0, import_viem.createWalletClient)({
account: (0, import_viem_utils.getLocalAccount)(0, { nonceManager: import_viem.nonceManager }),
chain,
transport
});
let simpleAccountFactory;
let verifyingPaymaster;
(0, import_vitest.beforeAll)(async () => {
simpleAccountFactory = import_setupERC4337Contracts.erc4337Contracts.simpleAccountFactory;
verifyingPaymaster = (await (0, import_setupERC4337Contracts.setupVerifyingPaymaster)(walletClient, {
verifyingSignerAddress: walletClient.account.address
})).address;
});
(0, import_vitest.test)("getVerifyingPaymasterHash", async () => {
const validUntil = Date.now() + 3600;
const validAfter = 0;
const paymasterDataUnsigned = (0, import_viem.encodeAbiParameters)(
[
{ name: "validUntil", type: "uint48" },
{ name: "validAfter", type: "uint48" }
],
[validUntil, validAfter]
);
const userOperation = {
sender: import_viem.zeroAddress,
nonce: 0n,
factory: import_viem.zeroAddress,
factoryData: "0x",
callData: "0x",
preVerificationGas: 0n,
verificationGasLimit: 0n,
callGasLimit: 0n,
maxFeePerGas: 0n,
maxPriorityFeePerGas: 0n,
signature: import_UserOperation.dummySignature,
paymaster: verifyingPaymaster,
paymasterVerificationGasLimit: 0n,
paymasterPostOpGasLimit: 0n,
paymasterData: paymasterDataUnsigned
};
const userOperationPacked = (0, import_PackedUserOperation.toPackedUserOperation)((0, import_UserOperation.encodeUserOp)(userOperation));
const hashExpected = await publicClient.readContract({
address: verifyingPaymaster,
abi: import_VerifyingPaymaster.VerifyingPaymaster.abi,
functionName: "getHash",
args: [userOperationPacked, validUntil, validAfter]
});
const hash = (0, import_VerifyingPaymaster2.getVerifyingPaymasterHash)({
chainId: publicClient.chain.id,
userOperation: userOperationPacked
});
(0, import_vitest.expect)(hash).toBe(hashExpected);
});
(0, import_vitest.describe)("Exec existing Simple Account", () => {
let account;
let simpleAccount;
(0, import_vitest.beforeEach)(async () => {
const privateKey = (0, import_accounts.generatePrivateKey)();
account = (0, import_accounts.privateKeyToAccount)(privateKey);
const simpleAccountAddress = (0, import_SimpleAccount.getSimpleAccountAddress)(
{
owner: account.address,
salt: 0n
},
{
factoryAddress: simpleAccountFactory,
proxyBytecode: import_ERC1967Proxy.ERC1967Proxy.bytecode
}
);
const simpleAccountFactoryData = (0, import_viem.encodeFunctionData)({
abi: import_SimpleAccountFactory.SimpleAccountFactory.abi,
functionName: "createAccount",
args: [account.address, 0n]
});
simpleAccount = {
address: simpleAccountAddress,
factoryData: simpleAccountFactoryData,
factoryAddress: simpleAccountFactory
};
const { request: createAccountRequest } = await publicClient.simulateContract({
account: walletClient.account,
address: simpleAccountFactory,
abi: import_SimpleAccountFactory.SimpleAccountFactory.abi,
functionName: "createAccount",
args: [account.address, 0n]
});
const createAccountHash = await walletClient.writeContract(createAccountRequest);
await publicClient.waitForTransactionReceipt({ hash: createAccountHash });
const fundSimpleAccountHash = await walletClient.sendTransaction({
to: simpleAccount.address,
value: 1n
});
await publicClient.waitForTransactionReceipt({ hash: fundSimpleAccountHash });
});
(0, import_vitest.test)("submitUserOp - manual", async () => {
const to = (0, import_accounts.privateKeyToAccount)((0, import_accounts.generatePrivateKey)()).address;
const value = 1n;
const data = "0x";
const callData = (0, import_viem.encodeFunctionData)({
abi: [
{
inputs: [
{ name: "dest", type: "address" },
{ name: "value", type: "uint256" },
{ name: "func", type: "bytes" }
],
name: "execute",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}
],
args: [to, value, data]
});
const validUntil = Date.now() + 3600;
const validAfter = 0;
const paymasterDataUnsigned = (0, import_viem.encodeAbiParameters)(
[
{ name: "validUntil", type: "uint48" },
{ name: "validAfter", type: "uint48" }
],
[validUntil, validAfter]
);
const paymasterDataDummySignature = (0, import_viem.concatHex)([paymasterDataUnsigned, import_UserOperation.dummySignature]);
const gasPrice = await publicClient.estimateFeesPerGas();
const userOpData = {
sender: simpleAccount.address,
nonce: 0n,
callData,
paymaster: verifyingPaymaster,
//Empty, will be replaced with signature
paymasterData: paymasterDataDummySignature,
maxFeePerGas: gasPrice.maxFeePerGas
};
const {
preVerificationGas,
verificationGasLimit,
callGasLimit,
paymasterVerificationGasLimit,
paymasterPostOpGasLimit
} = await (0, import_estimateUserOperationGas.estimateUserOperationGas)(
{ ...publicClient, entryPointSimulationsAddress: import_setupERC4337Contracts.erc4337Contracts.pimlicoEntrypointSimulations },
userOpData
);
const userOp = {
sender: simpleAccount.address,
nonce: 0n,
signature: "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c",
callData,
callGasLimit,
verificationGasLimit,
preVerificationGas,
maxFeePerGas: gasPrice.maxFeePerGas,
maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
paymaster: verifyingPaymaster,
//Empty, will be replaced with signature
paymasterData: paymasterDataDummySignature,
paymasterVerificationGasLimit,
paymasterPostOpGasLimit
};
const userOpPaymasterPacked = (0, import_PackedUserOperation.toPackedUserOperation)((0, import_UserOperation.encodeUserOp)(userOp));
const userOpPaymasterHash = await publicClient.readContract({
address: verifyingPaymaster,
abi: import_VerifyingPaymaster.VerifyingPaymaster.abi,
functionName: "getHash",
args: [userOpPaymasterPacked, validUntil, validAfter]
});
const paymasterSignature = await walletClient.account.signMessage({
message: {
raw: userOpPaymasterHash
}
});
const paymasterSignatureBytes = (0, import_viem.hexToBytes)(paymasterSignature);
(0, import_vitest.expect)(paymasterSignatureBytes.length).toBeGreaterThanOrEqual(64);
(0, import_vitest.expect)(paymasterSignatureBytes.length).toBeLessThanOrEqual(65);
const paymasterDataSigned = (0, import_viem.concatHex)([paymasterDataUnsigned, paymasterSignature]);
userOp.paymasterData = paymasterDataSigned;
const userOpHash = (0, import_account_abstraction.getUserOperationHash)({
userOperation: userOp,
entryPointAddress: import_account_abstraction.entryPoint07Address,
entryPointVersion: "0.7",
chainId: import_chains.localhost.id
});
const signature = await account.signMessage({
message: { raw: userOpHash }
});
userOp.signature = signature;
const userOpPacked = (0, import_PackedUserOperation.toPackedUserOperation)((0, import_UserOperation.encodeUserOp)(userOp));
const handleOpsArgs = [[userOpPacked], walletClient.account.address];
const paymasterDeposit = await publicClient.simulateContract({
account: walletClient.account,
address: verifyingPaymaster,
abi: import_VerifyingPaymaster.VerifyingPaymaster.abi,
functionName: "deposit",
value: (0, import_viem.parseEther)("10"),
args: []
});
const paymasterDepositHash = await walletClient.writeContract(paymasterDeposit.request);
await publicClient.waitForTransactionReceipt({ hash: paymasterDepositHash });
const { request } = await publicClient.simulateContract({
account: walletClient.account,
address: import_account_abstraction.entryPoint07Address,
abi: import_IEntryPoint.IEntryPoint.abi,
functionName: "handleOps",
args: handleOpsArgs
});
const handleOpsHash = await walletClient.writeContract(request);
await publicClient.waitForTransactionReceipt({ hash: handleOpsHash });
const balance = await publicClient.getBalance({ address: to });
(0, import_vitest.expect)(balance).toBe(value);
});
});
});