diff --git a/contracts/verifying/VerifyingSingletonPaymasterV2.sol b/contracts/verifying/VerifyingSingletonPaymasterV2.sol index ae2a3f1..940ea1b 100644 --- a/contracts/verifying/VerifyingSingletonPaymasterV2.sol +++ b/contracts/verifying/VerifyingSingletonPaymasterV2.sol @@ -52,7 +52,7 @@ contract VerifyingSingletonPaymasterV2 is sstore(verifyingSigner.slot, _verifyingSigner) sstore(feeCollector.slot, _feeCollector) } - unaccountedEPGasOverhead = 12000; + unaccountedEPGasOverhead = 24000; fixedPriceMarkup = 1100000; // 10% } @@ -130,6 +130,7 @@ contract VerifyingSingletonPaymasterV2 is */ function setFixedPriceMarkup(uint32 _markup) external payable onlyOwner { require(_markup <= PRICE_DENOMINATOR * 2, "Markup too high"); + require(_markup >= PRICE_DENOMINATOR, "Markup too low"); // if allowed that would mean discounted uint32 oldValue = fixedPriceMarkup; fixedPriceMarkup = _markup; emit FixedPriceMarkupChanged(oldValue, _markup); diff --git a/test/bundler-integration/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts b/test/bundler-integration/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts new file mode 100644 index 0000000..6745375 --- /dev/null +++ b/test/bundler-integration/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts @@ -0,0 +1,448 @@ +/* eslint-disable node/no-missing-import */ +/* eslint-disable camelcase */ +import { expect } from "chai"; +import { ethers } from "hardhat"; + +import { + BiconomyAccountImplementation, + BiconomyAccountImplementation__factory, + BiconomyAccountFactory, + BiconomyAccountFactory__factory, + VerifyingSingletonPaymasterV2, + VerifyingSingletonPaymasterV2__factory, +} from "../../../typechain-types"; +import { fillAndSign } from "../../utils/userOp"; +import { UserOperation } from "../../../lib/account-abstraction/test/UserOperation"; +import { + createAccount, + simulationResultCatch, +} from "../../../lib/account-abstraction/test/testutils"; +import { + EntryPoint, + EntryPoint__factory, + SimpleAccount, + TestToken, + TestToken__factory, +} from "../../../lib/account-abstraction/typechain"; +import { + EcdsaOwnershipRegistryModule, + EcdsaOwnershipRegistryModule__factory, +} from "@biconomy-devx/account-contracts-v2/dist/types"; +import { arrayify, hexConcat, parseEther } from "ethers/lib/utils"; +import { BigNumber, BigNumberish, Contract, Signer } from "ethers"; +import { + BundlerTestEnvironment, + EthSendUserOperationResult, + UserOperationSubmissionError, +} from "../environment/bundlerEnvironment"; +import { getUserOpEvent, parseEvent } from "../../utils/testUtils"; + +export const AddressZero = ethers.constants.AddressZero; + +const UserOperationEventTopic = + "0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f"; + +const MOCK_VALID_UNTIL = "0x00000000deadbeef"; +const MOCK_VALID_AFTER = "0x0000000000001234"; +const dynamicMarkup = 1200000; // or 0 or 1100000 + +export async function deployEntryPoint( + provider = ethers.provider +): Promise { + const epf = await (await ethers.getContractFactory("EntryPoint")).deploy(); + return EntryPoint__factory.connect(epf.address, provider.getSigner()); +} + +describe("EntryPoint with VerifyingPaymaster Singleton", function () { + let entryPoint: EntryPoint; + let entryPointStatic: EntryPoint; + let depositorSigner: Signer; + let walletOwner: Signer; + let walletAddress: string, paymasterAddress: string; + let ethersSigner; + + let offchainSigner: Signer, deployer: Signer, feeCollector: Signer; + + let verifyingSingletonPaymaster: VerifyingSingletonPaymasterV2; + let smartWalletImp: BiconomyAccountImplementation; + let ecdsaModule: EcdsaOwnershipRegistryModule; + let walletFactory: BiconomyAccountFactory; + const abi = ethers.utils.defaultAbiCoder; + + let environment: BundlerTestEnvironment; + + beforeEach(async function () { + // Setup the Bundler Environment + const chainId = (await ethers.provider.getNetwork()).chainId; + if (chainId !== BundlerTestEnvironment.BUNDLER_ENVIRONMENT_CHAIN_ID) { + this.skip(); + } + environment = await BundlerTestEnvironment.getDefaultInstance(); + + ethersSigner = await ethers.getSigners(); + entryPoint = EntryPoint__factory.connect(process.env.ENTRYPOINT!, deployer); + entryPointStatic = entryPoint.connect(AddressZero); + + deployer = ethersSigner[0]; + offchainSigner = ethersSigner[1]; + depositorSigner = ethersSigner[2]; + feeCollector = ethersSigner[3]; + walletOwner = deployer; // ethersSigner[0]; + + const offchainSignerAddress = await offchainSigner.getAddress(); + const walletOwnerAddress = await walletOwner.getAddress(); + const feeCollectorAddress = await feeCollector.getAddress(); + + ecdsaModule = await new EcdsaOwnershipRegistryModule__factory( + deployer + ).deploy(); + + verifyingSingletonPaymaster = + await new VerifyingSingletonPaymasterV2__factory(deployer).deploy( + await deployer.getAddress(), + entryPoint.address, + offchainSignerAddress, + feeCollectorAddress + ); + + smartWalletImp = await new BiconomyAccountImplementation__factory( + deployer + ).deploy(entryPoint.address); + + walletFactory = await new BiconomyAccountFactory__factory(deployer).deploy( + smartWalletImp.address, + walletOwnerAddress + ); + + await walletFactory + .connect(deployer) + .addStake(entryPoint.address, 86400, { value: parseEther("2") }); + + const ecdsaOwnershipSetupData = ecdsaModule.interface.encodeFunctionData( + "initForSmartAccount", + [walletOwnerAddress] + ); + + const smartAccountDeploymentIndex = 0; + + await walletFactory.deployCounterFactualAccount( + ecdsaModule.address, + ecdsaOwnershipSetupData, + smartAccountDeploymentIndex + ); + const expected = await walletFactory.getAddressForCounterFactualAccount( + ecdsaModule.address, + ecdsaOwnershipSetupData, + smartAccountDeploymentIndex + ); + + walletAddress = expected; + console.log(" wallet address ", walletAddress); + + paymasterAddress = verifyingSingletonPaymaster.address; + console.log("Paymaster address is ", paymasterAddress); + + await entryPoint + .connect(deployer) + .depositTo(paymasterAddress, { value: parseEther("1") }); + + await verifyingSingletonPaymaster.connect(deployer).addStake(86400, { + value: parseEther("10"), + }); + }); + + after(async function () { + const chainId = (await ethers.provider.getNetwork()).chainId; + if (chainId === BundlerTestEnvironment.BUNDLER_ENVIRONMENT_CHAIN_ID) { + await Promise.all([ + environment.revert(environment.defaultSnapshot!), + environment.resetBundler(), + ]); + } + }); + + describe("#validatePaymasterUserOp", () => { + it("Should parse data properly", async () => { + const paymasterAndData = hexConcat([ + paymasterAddress, + ethers.utils.defaultAbiCoder.encode( + ["address", "uint48", "uint48", "uint32"], + [ + await offchainSigner.getAddress(), + MOCK_VALID_UNTIL, + MOCK_VALID_AFTER, + dynamicMarkup, + ] + ), + "0x" + "00".repeat(65), + ]); + + const res = await verifyingSingletonPaymaster.parsePaymasterAndData( + paymasterAndData + ); + + expect(res.paymasterId).to.equal(await offchainSigner.getAddress()); + expect(res.validUntil).to.equal(ethers.BigNumber.from(MOCK_VALID_UNTIL)); + expect(res.validAfter).to.equal(ethers.BigNumber.from(MOCK_VALID_AFTER)); + expect(res.priceMarkup).to.equal(dynamicMarkup); + expect(res.signature).to.equal("0x" + "00".repeat(65)); + }); + + it("succeed with valid signature", async () => { + const feeCollectorBalanceBefore = + await verifyingSingletonPaymaster.getBalance( + await feeCollector.getAddress() + ); + expect(feeCollectorBalanceBefore).to.be.equal(BigNumber.from(0)); + const signer = await verifyingSingletonPaymaster.verifyingSigner(); + const offchainSignerAddress = await offchainSigner.getAddress(); + expect(signer).to.be.equal(offchainSignerAddress); + + await verifyingSingletonPaymaster.depositFor( + await offchainSigner.getAddress(), + { value: ethers.utils.parseEther("1") } + ); + const userOp1 = await fillAndSign( + { + sender: walletAddress, + verificationGasLimit: 200000, // for positive case 200k + preVerificationGas: 55000, // min expected by bundler is 46k + }, + walletOwner, + entryPoint, + "nonce" + ); + + const hash = await verifyingSingletonPaymaster.getHash( + userOp1, + await offchainSigner.getAddress(), + MOCK_VALID_UNTIL, + MOCK_VALID_AFTER, + dynamicMarkup + ); + const sig = await offchainSigner.signMessage(arrayify(hash)); + const userOp = await fillAndSign( + { + ...userOp1, + paymasterAndData: hexConcat([ + paymasterAddress, + ethers.utils.defaultAbiCoder.encode( + ["address", "uint48", "uint48", "uint32"], + [ + await offchainSigner.getAddress(), + MOCK_VALID_UNTIL, + MOCK_VALID_AFTER, + dynamicMarkup, + ] + ), + sig, + ]), + }, + walletOwner, + entryPoint, + "nonce" + ); + + const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode( + ["bytes", "address"], + [userOp.signature, ecdsaModule.address] + ); + userOp.signature = signatureWithModuleAddress; + + console.log("userop VGL ", userOp.verificationGasLimit.toString()); + console.log("userop PVG ", userOp.preVerificationGas.toString()); + + const result: EthSendUserOperationResult = + await environment.sendUserOperation(userOp, entryPoint.address); + + const receipt = (await environment.getUserOperationReceipt(result.result)) + .result; + + const event = parseEvent(receipt.receipt, UserOperationEventTopic); + + const eventLogs = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + // eslint-disable-next-line no-unused-expressions + expect(eventLogs.success).to.be.true; + + await expect( + entryPoint.handleOps([userOp], await offchainSigner.getAddress()) + ).to.be.reverted; + + const feeCollectorBalanceAfter = + await verifyingSingletonPaymaster.getBalance( + await feeCollector.getAddress() + ); + expect(feeCollectorBalanceAfter).to.be.greaterThan(BigNumber.from(0)); + }); + + it("fails if verificationGasLimit is not enough", async () => { + const feeCollectorBalanceBefore = + await verifyingSingletonPaymaster.getBalance( + await feeCollector.getAddress() + ); + expect(feeCollectorBalanceBefore).to.be.equal(BigNumber.from(0)); + const signer = await verifyingSingletonPaymaster.verifyingSigner(); + const offchainSignerAddress = await offchainSigner.getAddress(); + expect(signer).to.be.equal(offchainSignerAddress); + + await verifyingSingletonPaymaster.depositFor( + await offchainSigner.getAddress(), + { value: ethers.utils.parseEther("1") } + ); + const userOp1 = await fillAndSign( + { + sender: walletAddress, + verificationGasLimit: 50000, // for positive case 200k + preVerificationGas: 55000, // min expected by bundler is 46k + }, + walletOwner, + entryPoint, + "nonce" + ); + + const hash = await verifyingSingletonPaymaster.getHash( + userOp1, + await offchainSigner.getAddress(), + MOCK_VALID_UNTIL, + MOCK_VALID_AFTER, + dynamicMarkup + ); + const sig = await offchainSigner.signMessage(arrayify(hash)); + const userOp = await fillAndSign( + { + ...userOp1, + paymasterAndData: hexConcat([ + paymasterAddress, + ethers.utils.defaultAbiCoder.encode( + ["address", "uint48", "uint48", "uint32"], + [ + await offchainSigner.getAddress(), + MOCK_VALID_UNTIL, + MOCK_VALID_AFTER, + dynamicMarkup, + ] + ), + sig, + ]), + }, + walletOwner, + entryPoint, + "nonce" + ); + + const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode( + ["bytes", "address"], + [userOp.signature, ecdsaModule.address] + ); + userOp.signature = signatureWithModuleAddress; + + console.log("userop VGL ", userOp.verificationGasLimit.toString()); + console.log("userop PVG ", userOp.preVerificationGas.toString()); + + let thrownError: Error | null = null; + + try { + await environment.sendUserOperation(userOp, entryPoint.address); + } catch (e) { + thrownError = e as Error; + } + + const expectedError = new UserOperationSubmissionError( + '{"message":"account validation failed: AA40 over verificationGasLimit' + ); + + expect(thrownError).to.contain(expectedError); + + await expect( + entryPoint.handleOps([userOp], await offchainSigner.getAddress()) + ).to.be.reverted; + }); + + it("fails if preVerificationGas is not enough", async () => { + const feeCollectorBalanceBefore = + await verifyingSingletonPaymaster.getBalance( + await feeCollector.getAddress() + ); + expect(feeCollectorBalanceBefore).to.be.equal(BigNumber.from(0)); + const signer = await verifyingSingletonPaymaster.verifyingSigner(); + const offchainSignerAddress = await offchainSigner.getAddress(); + expect(signer).to.be.equal(offchainSignerAddress); + + await verifyingSingletonPaymaster.depositFor( + await offchainSigner.getAddress(), + { value: ethers.utils.parseEther("1") } + ); + const userOp1 = await fillAndSign( + { + sender: walletAddress, + verificationGasLimit: 200000, // for positive case 200k + // preVerificationGas: 55000, // min expected by bundler is 46k + }, + walletOwner, + entryPoint, + "nonce" + ); + + const hash = await verifyingSingletonPaymaster.getHash( + userOp1, + await offchainSigner.getAddress(), + MOCK_VALID_UNTIL, + MOCK_VALID_AFTER, + dynamicMarkup + ); + const sig = await offchainSigner.signMessage(arrayify(hash)); + const userOp = await fillAndSign( + { + ...userOp1, + paymasterAndData: hexConcat([ + paymasterAddress, + ethers.utils.defaultAbiCoder.encode( + ["address", "uint48", "uint48", "uint32"], + [ + await offchainSigner.getAddress(), + MOCK_VALID_UNTIL, + MOCK_VALID_AFTER, + dynamicMarkup, + ] + ), + sig, + ]), + }, + walletOwner, + entryPoint, + "nonce" + ); + + const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode( + ["bytes", "address"], + [userOp.signature, ecdsaModule.address] + ); + userOp.signature = signatureWithModuleAddress; + + console.log("userop VGL ", userOp.verificationGasLimit.toString()); + console.log("userop PVG ", userOp.preVerificationGas.toString()); + + let thrownError: Error | null = null; + + try { + await environment.sendUserOperation(userOp, entryPoint.address); + } catch (e) { + thrownError = e as Error; + } + + const expectedError = new UserOperationSubmissionError( + '{"message":"preVerificationGas too low: expected at least 45916' + ); + + expect(thrownError).to.contain(expectedError); + + // await expect( + // entryPoint.handleOps([userOp], await offchainSigner.getAddress()) + // ).to.be.reverted; + }); + }); +}); diff --git a/test/bundler-integration/token-paymaster/biconomy-token-paymaster-specs.ts b/test/bundler-integration/token-paymaster/biconomy-token-paymaster-specs.ts index 30557ae..905e234 100644 --- a/test/bundler-integration/token-paymaster/biconomy-token-paymaster-specs.ts +++ b/test/bundler-integration/token-paymaster/biconomy-token-paymaster-specs.ts @@ -31,7 +31,11 @@ import { } from "@biconomy-devx/account-contracts-v2/dist/types"; import { arrayify, parseEther } from "ethers/lib/utils"; import { BigNumber, BigNumberish, Signer } from "ethers"; -import { BundlerTestEnvironment } from "../environment/bundlerEnvironment"; +import { + BundlerTestEnvironment, + EthSendUserOperationResult, +} from "../environment/bundlerEnvironment"; +import { parseEvent } from "../../utils/testUtils"; export const AddressZero = ethers.constants.AddressZero; @@ -43,6 +47,9 @@ const DEFAULT_FEE_MARKUP = 1100000; const MOCK_FX: BigNumberish = "977100"; // matic to usdc approx +const UserOperationEventTopic = + "0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f"; + export async function deployEntryPoint( provider = ethers.provider ): Promise { @@ -68,14 +75,6 @@ export const encodePaymasterData = ( ); }; -export async function getUserOpEvent(ep: EntryPoint) { - const [log] = await ep.queryFilter( - ep.filters.UserOperationEvent(), - await ethers.provider.getBlockNumber() - ); - return log; -} - export const encodeERC20Approval = ( account: BiconomyAccountImplementation, token: TestToken, @@ -121,7 +120,7 @@ describe("Biconomy Token Paymaster (with Bundler)", function () { entryPoint = EntryPoint__factory.connect(process.env.ENTRYPOINT!, deployer); offchainSigner = ethersSigner[1]; - walletOwner = deployer; // ethersSigner[3]; + walletOwner = deployer; // ethersSigner[0]; // const offchainSignerAddress = await deployer.getAddress(); const walletOwnerAddress = await walletOwner.getAddress(); @@ -236,6 +235,9 @@ describe("Biconomy Token Paymaster (with Bundler)", function () { .connect(deployer) .transfer(walletAddress, ethers.utils.parseEther("100")); + const accountBalBefore = await token.balanceOf(walletAddress); + const feeReceiverBalBefore = await token.balanceOf(paymasterAddress); + const userOp1 = await fillAndSign( { sender: walletAddress, @@ -292,10 +294,26 @@ describe("Biconomy Token Paymaster (with Bundler)", function () { userOp.signature = signatureWithModuleAddress; - await environment.sendUserOperation(userOp, entryPoint.address); + const result: EthSendUserOperationResult = + await environment.sendUserOperation(userOp, entryPoint.address); + + const receipt = (await environment.getUserOperationReceipt(result.result)) + .result; + + const event = parseEvent(receipt.receipt, UserOperationEventTopic); + + const eventLogs = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + expect(eventLogs.success).to.be.true; + + const accountBalAfter = await token.balanceOf(walletAddress); + const feeReceiverBalAfter = await token.balanceOf(paymasterAddress); - const ev = await getUserOpEvent(entryPoint); - expect(ev.args.success).to.be.true; + expect(accountBalAfter).to.be.lt(accountBalBefore); + expect(feeReceiverBalAfter).to.be.gt(feeReceiverBalBefore); await expect( entryPoint.handleOps([userOp], await offchainSigner.getAddress()) diff --git a/test/bundler-integration/token-paymaster/btpm-undeployed-wallet.ts b/test/bundler-integration/token-paymaster/btpm-undeployed-wallet.ts index 01bf57e..1d9f8db 100644 --- a/test/bundler-integration/token-paymaster/btpm-undeployed-wallet.ts +++ b/test/bundler-integration/token-paymaster/btpm-undeployed-wallet.ts @@ -31,7 +31,8 @@ import { } from "../../../lib/account-abstraction/typechain"; import { arrayify, hexConcat, parseEther } from "ethers/lib/utils"; import { BigNumber, BigNumberish, Contract, Signer } from "ethers"; -import { BundlerTestEnvironment } from "../environment/bundlerEnvironment"; +import { BundlerTestEnvironment, EthSendUserOperationResult } from "../environment/bundlerEnvironment"; +import { getUserOpEvent, parseEvent } from "../../utils/testUtils"; export const AddressZero = ethers.constants.AddressZero; @@ -43,6 +44,9 @@ const DEFAULT_FEE_MARKUP = 1100000; const MOCK_FX: BigNumberish = "977100"; // matic to usdc approx +const UserOperationEventTopic = + "0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f"; + export async function deployEntryPoint( provider = ethers.provider ): Promise { @@ -68,14 +72,6 @@ export const encodePaymasterData = ( ); }; -export async function getUserOpEvent(ep: EntryPoint) { - const [log] = await ep.queryFilter( - ep.filters.UserOperationEvent(), - await ethers.provider.getBlockNumber() - ); - return log; -} - export const encodeERC20Approval = ( account: BiconomyAccountImplementation, token: TestToken, @@ -123,7 +119,7 @@ describe("Biconomy Token Paymaster (with Bundler)", function () { entryPoint = EntryPoint__factory.connect(process.env.ENTRYPOINT!, deployer); offchainSigner = ethersSigner[1]; - walletOwner = deployer; // ethersSigner[3]; + walletOwner = deployer; // ethersSigner[0]; // const offchainSignerAddress = await deployer.getAddress(); const walletOwnerAddress = await walletOwner.getAddress(); @@ -228,6 +224,9 @@ describe("Biconomy Token Paymaster (with Bundler)", function () { .connect(deployer) .transfer(walletAddress, ethers.utils.parseEther("100")); + const accountBalBefore = await token.balanceOf(walletAddress); + const feeReceiverBalBefore = await token.balanceOf(paymasterAddress); + const owner = await walletOwner.getAddress(); const AccountFactory = await ethers.getContractFactory( "SmartAccountFactory" @@ -306,10 +305,26 @@ describe("Biconomy Token Paymaster (with Bundler)", function () { userOp.signature = signatureWithModuleAddress; - await environment.sendUserOperation(userOp, entryPoint.address); + const result: EthSendUserOperationResult = + await environment.sendUserOperation(userOp, entryPoint.address); + + const receipt = (await environment.getUserOperationReceipt(result.result)) + .result; + + const event = parseEvent(receipt.receipt, UserOperationEventTopic); + + const eventLogs = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + expect(eventLogs.success).to.be.true; + + const accountBalAfter = await token.balanceOf(walletAddress); + const feeReceiverBalAfter = await token.balanceOf(paymasterAddress); - const ev = await getUserOpEvent(entryPoint); - expect(ev.args.success).to.be.true; + expect(accountBalAfter).to.be.lt(accountBalBefore); + expect(feeReceiverBalAfter).to.be.gt(feeReceiverBalBefore); await expect( entryPoint.handleOps([userOp], await offchainSigner.getAddress()) diff --git a/test/bundler-integration/token-paymaster/oracle-aggregator-specs.ts b/test/bundler-integration/token-paymaster/oracle-aggregator-specs.ts index 5f87455..e6de6cb 100644 --- a/test/bundler-integration/token-paymaster/oracle-aggregator-specs.ts +++ b/test/bundler-integration/token-paymaster/oracle-aggregator-specs.ts @@ -37,6 +37,7 @@ import { import { arrayify, parseEther } from "ethers/lib/utils"; import { BigNumber, BigNumberish, Contract, Signer } from "ethers"; import { BundlerTestEnvironment } from "../environment/bundlerEnvironment"; +import { getUserOpEvent, parseEvent } from "../../utils/testUtils"; export const AddressZero = ethers.constants.AddressZero; @@ -46,6 +47,9 @@ const DEFAULT_FEE_MARKUP = 1100000; const MOCK_FX: BigNumberish = "977100"; // matic to usdc approx +const UserOperationEventTopic = + "0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f"; + export const encodePaymasterData = ( feeToken = ethers.constants.AddressZero, oracleAggregator = ethers.constants.AddressZero, @@ -65,14 +69,6 @@ export const encodePaymasterData = ( ); }; -export async function getUserOpEvent(ep: EntryPoint) { - const [log] = await ep.queryFilter( - ep.filters.UserOperationEvent(), - await ethers.provider.getBlockNumber() - ); - return log; -} - export const encodeERC20Approval = ( account: BiconomyAccountImplementation, token: TestToken, @@ -122,7 +118,7 @@ describe("Biconomy Token Paymaster (With Bundler)", function () { entryPoint = EntryPoint__factory.connect(process.env.ENTRYPOINT!, deployer); offchainSigner = ethersSigner[1]; - walletOwner = deployer; // ethersSigner[3]; + walletOwner = deployer; // ethersSigner[0]; // const offchainSignerAddress = await deployer.getAddress(); const walletOwnerAddress = await walletOwner.getAddress(); @@ -349,8 +345,15 @@ describe("Biconomy Token Paymaster (With Bundler)", function () { transactionHash ); - const ev = await getUserOpEvent(entryPoint); - expect(ev.args.success).to.be.true; + const event = parseEvent(receipt, UserOperationEventTopic); + + const eventLogsUserop = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + // eslint-disable-next-line no-unused-expressions + expect(eventLogsUserop.success).to.be.true; const BiconomyTokenPaymaster = await ethers.getContractFactory( "BiconomyTokenPaymaster" @@ -472,8 +475,15 @@ describe("Biconomy Token Paymaster (With Bundler)", function () { transactionHash ); - const ev = await getUserOpEvent(entryPoint); - expect(ev.args.success).to.be.true; + const event = parseEvent(receipt, UserOperationEventTopic); + + const eventLogsUserop = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + // eslint-disable-next-line no-unused-expressions + expect(eventLogsUserop.success).to.be.true; const BiconomyTokenPaymaster = await ethers.getContractFactory( "BiconomyTokenPaymaster" @@ -591,8 +601,15 @@ describe("Biconomy Token Paymaster (With Bundler)", function () { transactionHash ); - const ev = await getUserOpEvent(entryPoint); - expect(ev.args.success).to.be.true; + const event = parseEvent(receipt, UserOperationEventTopic); + + const eventLogsUserop = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + // eslint-disable-next-line no-unused-expressions + expect(eventLogsUserop.success).to.be.true; const BiconomyTokenPaymaster = await ethers.getContractFactory( "BiconomyTokenPaymaster" diff --git a/test/sponsorship-paymaster/biconomy-verifying-paymaster-specs.ts b/test/sponsorship-paymaster/biconomy-verifying-paymaster-specs.ts index 66e8011..fb4bc67 100644 --- a/test/sponsorship-paymaster/biconomy-verifying-paymaster-specs.ts +++ b/test/sponsorship-paymaster/biconomy-verifying-paymaster-specs.ts @@ -70,7 +70,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { deployer = ethersSigner[0]; offchainSigner = ethersSigner[1]; depositorSigner = ethersSigner[2]; - walletOwner = deployer; // ethersSigner[3]; + walletOwner = deployer; // ethersSigner[0]; const offchainSignerAddress = await offchainSigner.getAddress(); const walletOwnerAddress = await walletOwner.getAddress(); diff --git a/test/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts b/test/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts index c6b50fd..dfc541d 100644 --- a/test/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts +++ b/test/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts @@ -35,7 +35,7 @@ export const AddressZero = ethers.constants.AddressZero; const MOCK_VALID_UNTIL = "0x00000000deadbeef"; const MOCK_VALID_AFTER = "0x0000000000001234"; -const dynamicMarkup = 1200000; // or 0 or 1100000 +const dynamicMarkup = 1100000; // or 0 or 1100000 export async function deployEntryPoint( provider = ethers.provider @@ -69,7 +69,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { offchainSigner = ethersSigner[1]; depositorSigner = ethersSigner[2]; feeCollector = ethersSigner[3]; - walletOwner = deployer; // ethersSigner[3]; + walletOwner = deployer; // ethersSigner[0]; const offchainSignerAddress = await offchainSigner.getAddress(); const walletOwnerAddress = await walletOwner.getAddress(); @@ -161,58 +161,41 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { ); } - describe("#validatePaymasterUserOp", () => { - it("Should parse data properly", async () => { - const paymasterAndData = hexConcat([ - paymasterAddress, - ethers.utils.defaultAbiCoder.encode( - ["address", "uint48", "uint48", "uint32"], - [ - await offchainSigner.getAddress(), - MOCK_VALID_UNTIL, - MOCK_VALID_AFTER, - dynamicMarkup, - ] - ), - "0x" + "00".repeat(65), - ]); - - const res = await verifyingSingletonPaymaster.parsePaymasterAndData( - paymasterAndData - ); - - expect(res.paymasterId).to.equal(await offchainSigner.getAddress()); - expect(res.validUntil).to.equal(ethers.BigNumber.from(MOCK_VALID_UNTIL)); - expect(res.validAfter).to.equal(ethers.BigNumber.from(MOCK_VALID_AFTER)); - expect(res.priceMarkup).to.equal(dynamicMarkup); - expect(res.signature).to.equal("0x" + "00".repeat(65)); - }); - + describe("Verifying paymaster basic positive tests", () => { it("Should Fail when there is no deposit for paymaster id", async () => { const paymasterId = await depositorSigner.getAddress(); console.log("paymaster Id ", paymasterId); const userOp = await getUserOpWithPaymasterInfo(paymasterId); - console.log("entrypoint ", entryPoint.address); + await expect( entryPoint.callStatic.simulateValidation(userOp) - // ).to.be.revertedWith("FailedOp"); - ).to.be.reverted; + ).to.be.revertedWithCustomError(entryPoint, "FailedOp"); }); it("succeed with valid signature", async () => { + const fundingId = await offchainSigner.getAddress(); + + await verifyingSingletonPaymaster + .connect(deployer) + .setUnaccountedEPGasOverhead(24000); + + await verifyingSingletonPaymaster.depositFor(fundingId, { + value: ethers.utils.parseEther("1"), + }); + + const paymasterFundsBefore = await entryPoint.balanceOf(paymasterAddress); + const paymasterIdBalanceBefore = + await verifyingSingletonPaymaster.getBalance(fundingId); const feeCollectorBalanceBefore = await verifyingSingletonPaymaster.getBalance( await feeCollector.getAddress() ); + console.log("feeCollectorBalanceBefore ", feeCollectorBalanceBefore); expect(feeCollectorBalanceBefore).to.be.equal(BigNumber.from(0)); const signer = await verifyingSingletonPaymaster.verifyingSigner(); const offchainSignerAddress = await offchainSigner.getAddress(); expect(signer).to.be.equal(offchainSignerAddress); - await verifyingSingletonPaymaster.depositFor( - await offchainSigner.getAddress(), - { value: ethers.utils.parseEther("1") } - ); const userOp1 = await fillAndSign( { sender: walletAddress, @@ -225,7 +208,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { const hash = await verifyingSingletonPaymaster.getHash( userOp1, - await offchainSigner.getAddress(), + fundingId, MOCK_VALID_UNTIL, MOCK_VALID_AFTER, dynamicMarkup @@ -238,12 +221,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { paymasterAddress, ethers.utils.defaultAbiCoder.encode( ["address", "uint48", "uint48", "uint32"], - [ - await offchainSigner.getAddress(), - MOCK_VALID_UNTIL, - MOCK_VALID_AFTER, - dynamicMarkup, - ] + [fundingId, MOCK_VALID_UNTIL, MOCK_VALID_AFTER, dynamicMarkup] ), sig, ]), @@ -259,8 +237,44 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { ); userOp.signature = signatureWithModuleAddress; - await entryPoint.handleOps([userOp], await offchainSigner.getAddress()); - // gas used VPM V2 162081 + const tx = await entryPoint.handleOps( + [userOp], + await offchainSigner.getAddress() + ); + const receipt = await tx.wait(); + console.log("gas used VPM V2 ", receipt.gasUsed.toString()); + console.log("gas price ", receipt.effectiveGasPrice.toString()); + + const bundlerPaid = receipt.effectiveGasPrice.mul(receipt.gasUsed); + console.log("bundler paid ", bundlerPaid.toString()); + + const paymasterFundsAfter = await entryPoint.balanceOf(paymasterAddress); + const paymasterIdBalanceAfter = + await verifyingSingletonPaymaster.getBalance(fundingId); + + const paymasterIdBalanceDiff = paymasterIdBalanceBefore.sub( + paymasterIdBalanceAfter + ); + console.log("paymasterIdBalanceDiff ", paymasterIdBalanceDiff.toString()); + + const paymasterFundsDiff = paymasterFundsBefore.sub(paymasterFundsAfter); + console.log("paymasterFundsDiff ", paymasterFundsDiff.toString()); + + // Review + // 10/11 actual gas used + /* const paymasterIdBalanceDiffWithoutPremium = paymasterIdBalanceDiff + .mul(BigNumber.from(10)) + .div(BigNumber.from(11)); + + console.log( + "paymasterIdBalanceDiffWithoutPremium ", + paymasterIdBalanceDiffWithoutPremium.toString() + ); */ + + expect(paymasterIdBalanceDiff.sub(paymasterFundsDiff)).to.be.greaterThan( + BigNumber.from(0) + ); + await expect( entryPoint.handleOps([userOp], await offchainSigner.getAddress()) ).to.be.reverted; @@ -270,6 +284,113 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { await feeCollector.getAddress() ); expect(feeCollectorBalanceAfter).to.be.greaterThan(BigNumber.from(0)); + + // 0.1 / 1.1 = actual gas used * 0.1 + expect(feeCollectorBalanceAfter).to.be.equal( + paymasterIdBalanceDiff.mul(BigNumber.from(1)).div(BigNumber.from(11)) + ); + }); + }); + + describe("Verifying Paymaster - read methods and state checks", () => { + it("Should parse data properly", async () => { + const paymasterAndData = hexConcat([ + paymasterAddress, + ethers.utils.defaultAbiCoder.encode( + ["address", "uint48", "uint48", "uint32"], + [ + await offchainSigner.getAddress(), + MOCK_VALID_UNTIL, + MOCK_VALID_AFTER, + dynamicMarkup, + ] + ), + "0x" + "00".repeat(65), + ]); + + const res = await verifyingSingletonPaymaster.parsePaymasterAndData( + paymasterAndData + ); + + expect(res.paymasterId).to.equal(await offchainSigner.getAddress()); + expect(res.validUntil).to.equal(ethers.BigNumber.from(MOCK_VALID_UNTIL)); + expect(res.validAfter).to.equal(ethers.BigNumber.from(MOCK_VALID_AFTER)); + expect(res.priceMarkup).to.equal(dynamicMarkup); + expect(res.signature).to.equal("0x" + "00".repeat(65)); + }); + + it("Invalid paymasterAndData causes revert", async () => { + const paymasterAndData = + "0x9c145aed00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000124468721a7000000000000000000000000831153c6b9537d0ff5b7db830c2749de3042e77600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000649b890b7000000000000000000000000071bc333f52a7971dde5de5b6c1ab0e7341e9724c00000000000000000000000021a6f9fa7246de45762e6f9db1f55e3c0f8566db00000000000000000000000000000000000000000000000000000000000000"; + + await expect( + verifyingSingletonPaymaster.parsePaymasterAndData(paymasterAndData) + ).to.be.reverted; + }); + + it("should check the correct states set on the paymaster", async () => { + const owner = await verifyingSingletonPaymaster.owner(); + + const verifyingSigner = + await verifyingSingletonPaymaster.verifyingSigner(); + + const feeReceiver = await verifyingSingletonPaymaster.feeCollector(); + + expect(owner).to.be.equal(deployer.address); + expect(verifyingSigner).to.be.equal(offchainSigner.address); + expect(feeReceiver).to.be.equal(feeCollector.address); + }); + }); + + describe("Verifying Paymaster - deposit and withdraw tests", () => { + it("Deposits into the specified address", async () => { + const paymasterId = await depositorSigner.getAddress(); + + await verifyingSingletonPaymaster.depositFor(paymasterId, { + value: parseEther("1"), + }); + + const balance = await verifyingSingletonPaymaster.getBalance(paymasterId); + expect(balance).to.be.equal(parseEther("1")); + }); + + it("Does not allow 0 value deposits", async () => { + const paymasterId = await depositorSigner.getAddress(); + + await expect( + verifyingSingletonPaymaster.depositFor(paymasterId, { + value: parseEther("0"), + }) + ).to.be.revertedWithCustomError( + verifyingSingletonPaymaster, + "DepositCanNotBeZero" + ); + }); + + it("Does not allow deposits for 0 address paymasterId", async () => { + const paymasterId = ethers.constants.AddressZero; + + await expect( + verifyingSingletonPaymaster.depositFor(paymasterId, { + value: parseEther("0.5"), + }) + ).to.be.revertedWithCustomError( + verifyingSingletonPaymaster, + "PaymasterIdCannotBeZero" + ); + }); + + it("Reverts when paymasterIdBalance is not enough", async () => { + const paymasterId = await depositorSigner.getAddress(); + + await expect( + verifyingSingletonPaymaster + .connect(depositorSigner) + .withdrawTo(paymasterId, parseEther("1.1")) + ).to.be.revertedWithCustomError( + verifyingSingletonPaymaster, + "InsufficientBalance" + ); }); }); }); diff --git a/test/token-paymaster/biconomy-token-paymaster-specs.ts b/test/token-paymaster/biconomy-token-paymaster-specs.ts index d5b7bc2..b09698b 100644 --- a/test/token-paymaster/biconomy-token-paymaster-specs.ts +++ b/test/token-paymaster/biconomy-token-paymaster-specs.ts @@ -40,6 +40,7 @@ import { import { arrayify, hexConcat, parseEther } from "ethers/lib/utils"; import { BigNumber, BigNumberish, Contract, Signer } from "ethers"; import { SignerWithAddress } from "hardhat-deploy-ethers/signers"; +import { getUserOpEvent, parseEvent } from "../utils/testUtils"; export const AddressZero = ethers.constants.AddressZero; @@ -51,6 +52,9 @@ const DEFAULT_FEE_MARKUP = 1100000; const MOCK_FX: BigNumberish = "977100"; // matic to usdc approx +const UserOperationEventTopic = + "0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f"; + export async function deployEntryPoint( provider = ethers.provider ): Promise { @@ -76,14 +80,6 @@ export const encodePaymasterData = ( ); }; -export async function getUserOpEvent(ep: EntryPoint) { - const [log] = await ep.queryFilter( - ep.filters.UserOperationEvent(), - await ethers.provider.getBlockNumber() - ); - return log; -} - export const encodeERC20Approval = ( account: BiconomyAccountImplementation, token: TestToken, @@ -127,7 +123,7 @@ describe("Biconomy Token Paymaster", function () { deployer = ethersSigner[0]; offchainSigner = ethersSigner[1]; depositorSigner = ethersSigner[2]; - walletOwner = deployer; // ethersSigner[3]; + walletOwner = deployer; // ethersSigner[0]; // const offchainSignerAddress = await deployer.getAddress(); const walletOwnerAddress = await walletOwner.getAddress(); @@ -366,8 +362,15 @@ describe("Biconomy Token Paymaster", function () { const postTokenBalanceForAccount = await token.balanceOf(walletAddress); - const ev = await getUserOpEvent(entryPoint); - expect(ev.args.success).to.be.true; + const event = parseEvent(receipt, UserOperationEventTopic); + + const eventLogsUserop = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + // eslint-disable-next-line no-unused-expressions + expect(eventLogsUserop.success).to.be.true; await expect( entryPoint.handleOps([userOp], await offchainSigner.getAddress()) @@ -680,13 +683,23 @@ describe("Biconomy Token Paymaster", function () { userOp.signature = signatureWithModuleAddress; - await entryPoint.handleOps([userOp], await offchainSigner.getAddress()); + const tx = await entryPoint.handleOps( + [userOp], + await offchainSigner.getAddress() + ); + const receipt = await tx.wait(); const postBalance = await token.balanceOf(paymasterAddress); - const ev = await getUserOpEvent(entryPoint); - // Review this because despite explicit revert bundler still pays gas - expect(ev.args.success).to.be.false; + const event = parseEvent(receipt, UserOperationEventTopic); + + const eventLogsUserop = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + // eslint-disable-next-line no-unused-expressions + expect(eventLogsUserop.success).to.be.false; await expect( entryPoint.handleOps([userOp], await offchainSigner.getAddress()) diff --git a/test/token-paymaster/btpm-coverage-specs.ts b/test/token-paymaster/btpm-coverage-specs.ts index 7282cf8..f077324 100644 --- a/test/token-paymaster/btpm-coverage-specs.ts +++ b/test/token-paymaster/btpm-coverage-specs.ts @@ -79,14 +79,6 @@ export const encodePaymasterData = ( ); }; -export async function getUserOpEvent(ep: EntryPoint) { - const [log] = await ep.queryFilter( - ep.filters.UserOperationEvent(), - await ethers.provider.getBlockNumber() - ); - return log; -} - export const encodeERC20Approval = ( account: BiconomyAccountImplementation, token: TestToken, @@ -130,7 +122,7 @@ describe("Biconomy Token Paymaster", function () { deployer = ethersSigner[0]; offchainSigner = ethersSigner[1]; depositorSigner = ethersSigner[2]; - walletOwner = deployer; // ethersSigner[3]; + walletOwner = deployer; // ethersSigner[0]; // const offchainSignerAddress = await deployer.getAddress(); const walletOwnerAddress = await walletOwner.getAddress(); diff --git a/test/token-paymaster/btpm-undeployed-wallet.ts b/test/token-paymaster/btpm-undeployed-wallet.ts index ddb5307..dc9144f 100644 --- a/test/token-paymaster/btpm-undeployed-wallet.ts +++ b/test/token-paymaster/btpm-undeployed-wallet.ts @@ -39,6 +39,7 @@ import { EcdsaOwnershipRegistryModule, EcdsaOwnershipRegistryModule__factory, } from "@biconomy-devx/account-contracts-v2/dist/types"; +import { getUserOpEvent, parseEvent } from "../utils/testUtils"; export const AddressZero = ethers.constants.AddressZero; const NATIVE_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; @@ -51,6 +52,9 @@ const DEFAULT_FEE_MARKUP = 1100000; const MOCK_FX: BigNumberish = "977100"; // matic to usdc approx +const UserOperationEventTopic = + "0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f"; + export async function deployEntryPoint( provider = ethers.provider ): Promise { @@ -76,14 +80,6 @@ export const encodePaymasterData = ( ); }; -export async function getUserOpEvent(ep: EntryPoint) { - const [log] = await ep.queryFilter( - ep.filters.UserOperationEvent(), - await ethers.provider.getBlockNumber() - ); - return log; -} - export const encodeERC20Approval = ( account: BiconomyAccountImplementation, token: TestToken, @@ -127,7 +123,7 @@ describe("Biconomy Token Paymaster", function () { deployer = ethersSigner[0]; offchainSigner = ethersSigner[1]; depositorSigner = ethersSigner[2]; - walletOwner = deployer; // ethersSigner[3]; + walletOwner = deployer; // ethersSigner[0]; // const offchainSignerAddress = await deployer.getAddress(); const walletOwnerAddress = await walletOwner.getAddress(); @@ -346,10 +342,17 @@ describe("Biconomy Token Paymaster", function () { await offchainSigner.getAddress() ); - await tx.wait(); + const receipt = await tx.wait(); + + const event = parseEvent(receipt, UserOperationEventTopic); + + const eventLogsUserop = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); - const ev = await getUserOpEvent(entryPoint); - expect(ev.args.success).to.be.true; + // eslint-disable-next-line no-unused-expressions + expect(eventLogsUserop.success).to.be.true; await expect( entryPoint.handleOps([userOp], await offchainSigner.getAddress()) diff --git a/test/token-paymaster/oracle-aggregator-specs.ts b/test/token-paymaster/oracle-aggregator-specs.ts index a9d0fc8..f6fa3f4 100644 --- a/test/token-paymaster/oracle-aggregator-specs.ts +++ b/test/token-paymaster/oracle-aggregator-specs.ts @@ -36,6 +36,7 @@ import { } from "@biconomy-devx/account-contracts-v2/dist/types"; import { arrayify, parseEther } from "ethers/lib/utils"; import { BigNumber, BigNumberish, Contract, Signer } from "ethers"; +import { getUserOpEvent, parseEvent } from "../utils/testUtils"; export const AddressZero = ethers.constants.AddressZero; @@ -45,6 +46,9 @@ const DEFAULT_FEE_MARKUP = 1100000; const MOCK_FX: BigNumberish = "977100"; // matic to usdc approx +const UserOperationEventTopic = + "0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f"; + export async function deployEntryPoint( provider = ethers.provider ): Promise { @@ -70,14 +74,6 @@ export const encodePaymasterData = ( ); }; -export async function getUserOpEvent(ep: EntryPoint) { - const [log] = await ep.queryFilter( - ep.filters.UserOperationEvent(), - await ethers.provider.getBlockNumber() - ); - return log; -} - export const encodeERC20Approval = ( account: BiconomyAccountImplementation, token: TestToken, @@ -122,7 +118,7 @@ describe("Biconomy Token Paymaster", function () { deployer = ethersSigner[0]; offchainSigner = ethersSigner[1]; depositorSigner = ethersSigner[2]; - walletOwner = deployer; // ethersSigner[3]; + walletOwner = deployer; // ethersSigner[0]; // const offchainSignerAddress = await deployer.getAddress(); const walletOwnerAddress = await walletOwner.getAddress(); @@ -336,10 +332,18 @@ describe("Biconomy Token Paymaster", function () { [userOp], await offchainSigner.getAddress() ); + const receipt = await tx.wait(); - const ev = await getUserOpEvent(entryPoint); - expect(ev.args.success).to.be.true; + const event = parseEvent(receipt, UserOperationEventTopic); + + const eventLogsUserop = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + // eslint-disable-next-line no-unused-expressions + expect(eventLogsUserop.success).to.be.true; const BiconomyTokenPaymaster = await ethers.getContractFactory( "BiconomyTokenPaymaster" @@ -452,8 +456,15 @@ describe("Biconomy Token Paymaster", function () { ); const receipt = await tx.wait(); - const ev = await getUserOpEvent(entryPoint); - expect(ev.args.success).to.be.true; + const event = parseEvent(receipt, UserOperationEventTopic); + + const eventLogsUserop = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + // eslint-disable-next-line no-unused-expressions + expect(eventLogsUserop.success).to.be.true; const BiconomyTokenPaymaster = await ethers.getContractFactory( "BiconomyTokenPaymaster" @@ -562,8 +573,15 @@ describe("Biconomy Token Paymaster", function () { ); const receipt = await tx.wait(); - const ev = await getUserOpEvent(entryPoint); - expect(ev.args.success).to.be.true; + const event = parseEvent(receipt, UserOperationEventTopic); + + const eventLogsUserop = entryPoint.interface.decodeEventLog( + "UserOperationEvent", + event[0].data + ); + + // eslint-disable-next-line no-unused-expressions + expect(eventLogsUserop.success).to.be.true; const BiconomyTokenPaymaster = await ethers.getContractFactory( "BiconomyTokenPaymaster" diff --git a/test/utils/testUtils.ts b/test/utils/testUtils.ts index 76b443d..ac6a700 100644 --- a/test/utils/testUtils.ts +++ b/test/utils/testUtils.ts @@ -6,6 +6,7 @@ import { parseEther, // solidityKeccak256, } from "ethers/lib/utils"; +import { TransactionReceipt } from "@ethersproject/providers"; import { BigNumber, // BigNumberish, @@ -23,6 +24,7 @@ import { // import { expect } from "chai"; // import { debugTransaction } from "./debugTx"; import { keccak256 } from "ethereumjs-util"; +import { EntryPoint } from "../../lib/account-abstraction/typechain"; export const AddressZero = ethers.constants.AddressZero; export const HashZero = ethers.constants.HashZero; @@ -60,6 +62,20 @@ export async function getBalance(address: string): Promise { return parseInt(balance.toString()); } +export async function getUserOpEvent(ep: EntryPoint) { + const [log] = await ep.queryFilter( + ep.filters.UserOperationEvent(), + await ethers.provider.getBlockNumber() + ); + return log; +} + +export function parseEvent(receipt: TransactionReceipt, topicName: string) { + return receipt.logs + .map((log) => log) + .filter((log) => log.topics[0] === topicName); +} + export function rethrow(): (e: Error) => void { const callerStack = new Error().stack ?.replace(/Error.*\n.*at.*\n/, "")