diff --git a/.env.example b/.env.example index 8e2cf2a..7779719 100644 --- a/.env.example +++ b/.env.example @@ -10,6 +10,7 @@ PAYMASTER_OWNER_ADDRESS_DEV= PAYMASTER_OWNER_ADDRESS_PROD= PAYMASTER_SIGNER_ADDRESS_DEV= PAYMASTER_SIGNER_ADDRESS_PROD= +SPONSORSHIP_FEE_COLLECTOR_ADDRESS_PROD= TOKEN_PAYMASTER_OWNER_ADDRESS_DEV= TOKEN_PAYMASTER_OWNER_ADDRESS_PROD= TOKEN_PAYMASTER_SIGNER_ADDRESS_DEV= diff --git a/contracts/common/Errors.sol b/contracts/common/Errors.sol index 2793af8..4e4fad2 100644 --- a/contracts/common/Errors.sol +++ b/contracts/common/Errors.sol @@ -9,7 +9,7 @@ contract BasePaymasterErrors { error CallerIsNotAnEntryPoint(address caller); } -contract VerifyingPaymasterErrors { +contract SponsorshipPaymasterErrors { /** * @notice Throws when the Entrypoint address provided is address(0) */ @@ -20,6 +20,16 @@ contract VerifyingPaymasterErrors { */ error VerifyingSignerCannotBeZero(); + /** + * @notice Throws when the paymaster id provided is a deployed contract + */ + error PaymasterIdCannotBeContract(); + + /** + * @notice Throws when the fee collector address provided is a deployed contract + */ + error FeeCollectorCannotBeContract(); + /** * @notice Throws when the fee collector address provided is address(0) */ diff --git a/contracts/interfaces/paymasters/IVerifyingSingletonPaymaster.sol b/contracts/interfaces/paymasters/ISponsorshipPaymaster.sol similarity index 97% rename from contracts/interfaces/paymasters/IVerifyingSingletonPaymaster.sol rename to contracts/interfaces/paymasters/ISponsorshipPaymaster.sol index 0ea4414..b777f2c 100644 --- a/contracts/interfaces/paymasters/IVerifyingSingletonPaymaster.sol +++ b/contracts/interfaces/paymasters/ISponsorshipPaymaster.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -interface IVerifyingSingletonPaymaster { +interface ISponsorshipPaymaster { event EPGasOverheadChanged( uint256 indexed _oldValue, uint256 indexed _newValue diff --git a/contracts/libs/AddressUtils.sol b/contracts/libs/AddressUtils.sol new file mode 100644 index 0000000..43cb832 --- /dev/null +++ b/contracts/libs/AddressUtils.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + + +/** + * Utility library of inline functions on addresses + */ +library AddressUtils { + + /** + * Returns whether the target address is a contract + * @dev This function will return false if invoked during the constructor of a contract, + * as the code is not actually created until after the constructor finishes. + * @param _addr address to check + * @return whether the target address is a contract + */ + function isContract(address _addr) internal view returns (bool) { + uint256 size; + // XXX Currently there is no better way to check if there is a contract in an address + // than to check the size of the code at that address. + // See https://ethereum.stackexchange.com/a/14016/36603 + // for more details about how this works. + // TODO Check this again before the Serenity release, because all addresses will be + // contracts then. + // solium-disable-next-line security/no-inline-assembly + assembly { size := extcodesize(_addr) } + return size > 0; + } + +} \ No newline at end of file diff --git a/contracts/verifying/PaymasterHelpers.sol b/contracts/sponsorship/PaymasterHelpers.sol similarity index 100% rename from contracts/verifying/PaymasterHelpers.sol rename to contracts/sponsorship/PaymasterHelpers.sol diff --git a/contracts/verifying/VerifyingSingletonPaymasterV2.sol b/contracts/sponsorship/SponsorshipPaymaster.sol similarity index 91% rename from contracts/verifying/VerifyingSingletonPaymasterV2.sol rename to contracts/sponsorship/SponsorshipPaymaster.sol index 940ea1b..89b2c00 100644 --- a/contracts/verifying/VerifyingSingletonPaymasterV2.sol +++ b/contracts/sponsorship/SponsorshipPaymaster.sol @@ -7,17 +7,30 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import {UserOperation, UserOperationLib} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; import "../BasePaymaster.sol"; -import {VerifyingPaymasterErrors} from "../common/Errors.sol"; +import {SponsorshipPaymasterErrors} from "../common/Errors.sol"; import {MathLib} from "../libs/MathLib.sol"; -import {IVerifyingSingletonPaymaster} from "../interfaces/paymasters/IVerifyingSingletonPaymaster.sol"; - -contract VerifyingSingletonPaymasterV2 is +import {AddressUtils} from "../libs/AddressUtils.sol"; +import {ISponsorshipPaymaster} from "../interfaces/paymasters/ISponsorshipPaymaster.sol"; + +/** + * @title SponsorshipPaymaster + * @author livingrockrises + * @notice Based on Infinitism 'VerifyingPaymaster' contract + * @dev This contract is used to sponsor the transaction fees of the user operations + * Uses a verifying signer to provide the signature if predetermined conditions are met + * regarding the user operation calldata. Also this paymaster is Singleton in nature which + * means multiple Dapps/Wallet clients willing to sponsor the transactions can share this paymaster. + * Maintains it's own accounting of the gas balance for each Dapp/Wallet client + * and Manages it's own deposit on the EntryPoint. + */ +contract SponsorshipPaymaster is BasePaymaster, ReentrancyGuard, - VerifyingPaymasterErrors, - IVerifyingSingletonPaymaster + SponsorshipPaymasterErrors, + ISponsorshipPaymaster { using ECDSA for bytes32; + using AddressUtils for address; using UserOperationLib for UserOperation; uint32 private constant PRICE_DENOMINATOR = 1e6; @@ -61,11 +74,10 @@ contract VerifyingSingletonPaymasterV2 is * @param paymasterId dapp identifier for which deposit is being made */ function depositFor(address paymasterId) external payable nonReentrant { + if(paymasterId.isContract()) revert PaymasterIdCannotBeContract(); if (paymasterId == address(0)) revert PaymasterIdCannotBeZero(); if (msg.value == 0) revert DepositCanNotBeZero(); - paymasterIdBalances[paymasterId] = - paymasterIdBalances[paymasterId] + - msg.value; + paymasterIdBalances[paymasterId] += msg.value; entryPoint.depositTo{value: msg.value}(address(this)); emit GasDeposited(paymasterId, msg.value); } @@ -99,6 +111,7 @@ contract VerifyingSingletonPaymasterV2 is function setFeeCollector( address _newFeeCollector ) external payable onlyOwner { + if(_newFeeCollector.isContract()) revert FeeCollectorCannotBeContract(); if (_newFeeCollector == address(0)) revert FeeCollectorCannotBeZero(); address oldFeeCollector = feeCollector; assembly { @@ -313,7 +326,6 @@ contract VerifyingSingletonPaymasterV2 is require(priceMarkup <= 2e6, "Verifying PM:high markup %"); uint32 dynamicMarkup = MathLib.maxuint32(priceMarkup, fixedPriceMarkup); - require(dynamicMarkup >= 1e6, "Verifying PM:low markup %"); uint256 effectiveCost = (requiredPreFund * dynamicMarkup) / PRICE_DENOMINATOR; diff --git a/contracts/verifying/VerifyingSingletonPaymaster.sol b/contracts/verifying/VerifyingSingletonPaymaster.sol deleted file mode 100644 index 9dfe266..0000000 --- a/contracts/verifying/VerifyingSingletonPaymaster.sol +++ /dev/null @@ -1,286 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.20; - -/* solhint-disable reason-string */ -/* solhint-disable no-inline-assembly */ -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import {UserOperation, UserOperationLib} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; -import "../BasePaymaster.sol"; -import {PaymasterHelpers, PaymasterData, PaymasterContext} from "./PaymasterHelpers.sol"; -import {VerifyingPaymasterErrors} from "../common/Errors.sol"; - -/** - * @title A sample paymaster that uses external service to decide whether to pay for the UserOp. - * @dev The paymaster trusts an external signer to sign the transaction. - * The calling user must pass the UserOp to that external signer first, which performs whatever - * off-chain verification before signing the UserOp. - * @notice That this signature is NOT a replacement for wallet signature: - * - The paymaster signs to agree to PAY for GAS. - * - The wallet signs to prove identity and wallet ownership. - */ -contract VerifyingSingletonPaymaster is - BasePaymaster, - ReentrancyGuard, - VerifyingPaymasterErrors -{ - using ECDSA for bytes32; - using UserOperationLib for UserOperation; - using PaymasterHelpers for UserOperation; - using PaymasterHelpers for bytes; - using PaymasterHelpers for PaymasterData; - - // Gas used in EntryPoint._handlePostOp() method (including this#postOp() call) - uint256 private unaccountedEPGasOverhead; - mapping(address => uint256) public paymasterIdBalances; - - address public verifyingSigner; - - event EPGasOverheadChanged( - uint256 indexed _oldValue, - uint256 indexed _newValue - ); - - event VerifyingSignerChanged( - address indexed _oldSigner, - address indexed _newSigner, - address indexed _actor - ); - event GasDeposited(address indexed _paymasterId, uint256 indexed _value); - event GasWithdrawn( - address indexed _paymasterId, - address indexed _to, - uint256 indexed _value - ); - event GasBalanceDeducted( - address indexed _paymasterId, - uint256 indexed _charge - ); - - constructor( - address _owner, - IEntryPoint _entryPoint, - address _verifyingSigner - ) payable BasePaymaster(_owner, _entryPoint) { - if (address(_entryPoint) == address(0)) revert EntryPointCannotBeZero(); - if (_verifyingSigner == address(0)) - revert VerifyingSignerCannotBeZero(); - assembly { - sstore(verifyingSigner.slot, _verifyingSigner) - } - unaccountedEPGasOverhead = 12000; - } - - /** - * @dev Add a deposit for this paymaster and given paymasterId (Dapp Depositor address), used for paying for transaction fees - * @param paymasterId dapp identifier for which deposit is being made - */ - function depositFor(address paymasterId) external payable nonReentrant { - if (paymasterId == address(0)) revert PaymasterIdCannotBeZero(); - if (msg.value == 0) revert DepositCanNotBeZero(); - paymasterIdBalances[paymasterId] = - paymasterIdBalances[paymasterId] + - msg.value; - entryPoint.depositTo{value: msg.value}(address(this)); - emit GasDeposited(paymasterId, msg.value); - } - - /** - * @dev Set a new verifying signer address. - * Can only be called by the owner of the contract. - * @param _newVerifyingSigner The new address to be set as the verifying signer. - * @notice If _newVerifyingSigner is set to zero address, it will revert with an error. - * After setting the new signer address, it will emit an event VerifyingSignerChanged. - */ - function setSigner(address _newVerifyingSigner) external payable onlyOwner { - if (_newVerifyingSigner == address(0)) - revert VerifyingSignerCannotBeZero(); - address oldSigner = verifyingSigner; - assembly { - sstore(verifyingSigner.slot, _newVerifyingSigner) - } - emit VerifyingSignerChanged(oldSigner, _newVerifyingSigner, msg.sender); - } - - function setUnaccountedEPGasOverhead(uint256 value) external onlyOwner { - uint256 oldValue = unaccountedEPGasOverhead; - unaccountedEPGasOverhead = value; - emit EPGasOverheadChanged(oldValue, value); - } - - /** - * @dev get the current deposit for paymasterId (Dapp Depositor address) - * @param paymasterId dapp identifier - */ - function getBalance( - address paymasterId - ) external view returns (uint256 balance) { - balance = paymasterIdBalances[paymasterId]; - } - - /** - @dev Override the default implementation. - */ - function deposit() public payable virtual override { - revert("use depositFor() instead"); - } - - /** - * @dev Withdraws the specified amount of gas tokens from the paymaster's balance and transfers them to the specified address. - * @param withdrawAddress The address to which the gas tokens should be transferred. - * @param amount The amount of gas tokens to withdraw. - */ - function withdrawTo( - address payable withdrawAddress, - uint256 amount - ) public override nonReentrant { - if (withdrawAddress == address(0)) revert CanNotWithdrawToZeroAddress(); - uint256 currentBalance = paymasterIdBalances[msg.sender]; - if (amount > currentBalance) - revert InsufficientBalance(amount, currentBalance); - paymasterIdBalances[msg.sender] = - paymasterIdBalances[msg.sender] - - amount; - entryPoint.withdrawTo(withdrawAddress, amount); - emit GasWithdrawn(msg.sender, withdrawAddress, amount); - } - - /** - * @dev This method is called by the off-chain service, to sign the request. - * It is called on-chain from the validatePaymasterUserOp, to validate the signature. - * @notice That this signature covers all fields of the UserOperation, except the "paymasterAndData", - * which will carry the signature itself. - * @return hash we're going to sign off-chain (and validate on-chain) - */ - function getHash( - UserOperation calldata userOp, - address paymasterId, - uint48 validUntil, - uint48 validAfter - ) public view returns (bytes32) { - //can't use userOp.hash(), since it contains also the paymasterAndData itself. - address sender = userOp.getSender(); - return - keccak256( - abi.encode( - sender, - userOp.nonce, - keccak256(userOp.initCode), - keccak256(userOp.callData), - userOp.callGasLimit, - userOp.verificationGasLimit, - userOp.preVerificationGas, - userOp.maxFeePerGas, - userOp.maxPriorityFeePerGas, - block.chainid, - address(this), - paymasterId, - validUntil, - validAfter - ) - ); - } - - /** - * @dev Executes the paymaster's payment conditions - * @param context payment conditions signed by the paymaster in `validatePaymasterUserOp` - * @param actualGasCost amount to be paid to the entry point in wei - */ - function _postOp( - PostOpMode /** mode */, - bytes calldata context, - uint256 actualGasCost - ) internal virtual override { - PaymasterContext memory data = context._decodePaymasterContext(); - address extractedPaymasterId = data.paymasterId; - uint256 effectiveGasPrice = getGasPrice( - data.maxFeePerGas, - data.maxPriorityFeePerGas - ); - uint256 balToDeduct = actualGasCost + - unaccountedEPGasOverhead * - effectiveGasPrice; - paymasterIdBalances[extractedPaymasterId] = - paymasterIdBalances[extractedPaymasterId] - - balToDeduct; - emit GasBalanceDeducted(extractedPaymasterId, balToDeduct); - } - - /** - * @dev Verify that an external signer signed the paymaster data of a user operation. - * The paymaster data is expected to be the paymaster and a signature over the entire request parameters. - * @param userOp The UserOperation struct that represents the current user operation. - * userOpHash The hash of the UserOperation struct. - * @param requiredPreFund The required amount of pre-funding for the paymaster. - * @return context A context string returned by the entry point after successful validation. - * @return validationData An integer returned by the entry point after successful validation. - */ - function _validatePaymasterUserOp( - UserOperation calldata userOp, - bytes32 /*userOpHash*/, - uint256 requiredPreFund - ) - internal - view - override - returns (bytes memory context, uint256 validationData) - { - PaymasterData memory paymasterData = userOp._decodePaymasterData(); - bytes32 hash = getHash( - userOp, - paymasterData.paymasterId, - paymasterData.validUntil, - paymasterData.validAfter - ); - uint256 sigLength = paymasterData.signatureLength; - // "require" is here so for the revert reason on invalid signature will be of "VerifyingPaymaster", and not "ECDSA" - if (sigLength != 65) revert InvalidPaymasterSignatureLength(sigLength); - //don't revert on signature failure: return SIG_VALIDATION_FAILED - if ( - verifyingSigner != - hash.toEthSignedMessageHash().recover(paymasterData.signature) - ) { - // empty context and sigFailed with time range provided - return ( - "", - _packValidationData( - true, - paymasterData.validUntil, - paymasterData.validAfter - ) - ); - } - if (requiredPreFund > paymasterIdBalances[paymasterData.paymasterId]) - revert InsufficientBalance( - requiredPreFund, - paymasterIdBalances[paymasterData.paymasterId] - ); - return ( - userOp.paymasterContext( - paymasterData, - userOp.maxFeePerGas, - userOp.maxPriorityFeePerGas - ), - _packValidationData( - false, - paymasterData.validUntil, - paymasterData.validAfter - ) - ); - } - - function getGasPrice( - uint256 maxFeePerGas, - uint256 maxPriorityFeePerGas - ) internal view returns (uint256) { - if (maxFeePerGas == maxPriorityFeePerGas) { - //legacy mode (for networks that don't support basefee opcode) - return maxFeePerGas; - } - return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); - } - - function min(uint256 a, uint256 b) internal pure returns (uint256) { - return a < b ? a : b; - } -} diff --git a/scripts/8-deploy-verifying-paymaster.ts b/scripts/8-deploy-verifying-paymaster.ts index 1cd76e9..177ebf2 100644 --- a/scripts/8-deploy-verifying-paymaster.ts +++ b/scripts/8-deploy-verifying-paymaster.ts @@ -12,6 +12,7 @@ const entryPointAddress = process.env.ENTRY_POINT_ADDRESS || "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; const verifyingSigner = process.env.PAYMASTER_SIGNER_ADDRESS_PROD || ""; +const feeCollector = process.env.SPONSORSHIP_FEE_COLLECTOR_ADDRESS_PROD || ""; const DEPLOYER_CONTRACT_ADDRESS = process.env.DEPLOYER_CONTRACT_ADDRESS_PROD || ""; @@ -23,60 +24,74 @@ function delay(ms: number) { }); } -async function deployVerifyingPaymasterContract( +async function deploySponsorshipPaymasterContract( deployerInstance: Deployer, earlyOwnerAddress: string ): Promise { try { const salt = ethers.utils.keccak256( - ethers.utils.toUtf8Bytes(DEPLOYMENT_SALTS.SINGELTON_PAYMASTER) + ethers.utils.toUtf8Bytes(DEPLOYMENT_SALTS.SPONSORSHIP_PAYMASTER) ); - const BiconomyVerifyingPaymaster = await ethers.getContractFactory( - "VerifyingSingletonPaymaster" + const SponsorshipPaymaster = await ethers.getContractFactory( + "SponsorshipPaymaster" ); - const verifyingPaymasterBytecode = `${ - BiconomyVerifyingPaymaster.bytecode + + const sponsorshipPaymasterBytecode = `${ + SponsorshipPaymaster.bytecode }${encodeParam("address", earlyOwnerAddress).slice(2)}${encodeParam( "address", entryPointAddress - ).slice(2)}${encodeParam("address", verifyingSigner).slice(2)}`; + ).slice(2)}${encodeParam("address", verifyingSigner).slice(2)}${encodeParam( + "address", + feeCollector + ).slice(2)}`; - const verifyingPaymasterComputedAddr = await deployerInstance.addressOf( + const sponsorshipPaymasterComputedAddr = await deployerInstance.addressOf( salt ); console.log( - "Verifying paymaster Computed Address: ", - verifyingPaymasterComputedAddr + "Sponsorship paymaster Computed Address: ", + sponsorshipPaymasterComputedAddr ); const isContractDeployed = await isContract( - verifyingPaymasterComputedAddr, + sponsorshipPaymasterComputedAddr, provider ); if (!isContractDeployed) { await deployContract( - DEPLOYMENT_SALTS.SINGELTON_PAYMASTER, - verifyingPaymasterComputedAddr, + DEPLOYMENT_SALTS.SPONSORSHIP_PAYMASTER, + sponsorshipPaymasterComputedAddr, salt, - verifyingPaymasterBytecode, + sponsorshipPaymasterBytecode, deployerInstance ); await delay(5000); await run(`verify:verify`, { - address: verifyingPaymasterComputedAddr, + address: sponsorshipPaymasterComputedAddr, constructorArguments: [ earlyOwnerAddress, entryPointAddress, verifyingSigner, + feeCollector, ], }); } else { + await run(`verify:verify`, { + address: sponsorshipPaymasterComputedAddr, + constructorArguments: [ + earlyOwnerAddress, + entryPointAddress, + verifyingSigner, + feeCollector, + ], + }); console.log( - "Verifying Paymaster is Already deployed with address ", - verifyingPaymasterComputedAddr + "Sponsorship Paymaster is Already deployed with address ", + sponsorshipPaymasterComputedAddr ); } - return verifyingPaymasterComputedAddr; + return sponsorshipPaymasterComputedAddr; } catch (err) { console.log(err); } @@ -114,13 +129,13 @@ async function main() { console.log("========================================="); // Deploy Verifying paymaster - const verifyingPaymasterAddress = await deployVerifyingPaymasterContract( + const sponsorshipPaymasterAddress = await deploySponsorshipPaymasterContract( deployerInstance, earlyOwner ); console.log( - "==================verifyingPaymasterAddress=======================", - verifyingPaymasterAddress + "==================sponsorshipPaymasterAddress=======================", + sponsorshipPaymasterAddress ); } diff --git a/scripts/utils/index.ts b/scripts/utils/index.ts index 629be36..03b2ba3 100644 --- a/scripts/utils/index.ts +++ b/scripts/utils/index.ts @@ -33,25 +33,6 @@ export const factoryTxHash = const factoryDeploymentFee = (0.0247 * 1e18).toString(); // 0.0247 const options = { gasLimit: 7000000 /*, gasPrice: 70000000000 */ }; -// TODO -// remove TEST for production deployments - -// 0xD3f89753278E419c8bda1eFe1366206B3D30C44f : Deployer address -/* export enum DEPLOYMENT_SALTS { // DEV - ORACLE_AGGREGATOR = "DEVX_CHAINLINK_ORACLE_AGGREGATOR_V0_27062023_bBee55b", // 0x0000065b8abb967271817555f23945eedf08015c - TOKEN_PAYMASTER = "DEVX_TOKEN_PAYMASTER_V0_08072023_h5AFKLa", // 0x0000023d6c240ae3c9610d519510004d2616c9ec - PRICE_FEED_USDC = "DEVX_PRICE_FEED_USDC_V0_27062023_uiaqdyv", // 0x000005abae3deadbe1fbd12105f950efba9eaec4 - PRICE_FEED_USDT = "DEVX_PRICE_FEED_USDT_V0_27062023_dIos1Nw", // 0x000001e2c2b39542c30a3fe57c4487030bc03adf - PRICE_FEED_DAI = "DEVX_PRICE_FEED_DAI_V0_27062023_1m7JNWQ", // 0x000000d2da196474046c7a4d94ab0e566b26d054 - PRICE_FEED_SAND = "DEVX_PRICE_FEED_SAND_V0_27062023_eHPqgeR", // 0x00000980455efb131c1908efa908c1eaf19db352 - PRICE_FEED_AAVE = "DEVX_PRICE_FEED_AAVE_V0_28062023_HsugnpY", // 0x0000af22a276f86b405835c18863e1c2a679d9e3 - PRICE_FEED_CAKE = "DEVX_PRICE_FEED_CAKE_V0_27062023_1BKpzde", // 0x000007198738f877c01d9b7e4a4e1bd17e46e4a8 - PRICE_FEED_LINK = "DEVX_PRICE_FEED_LINK_V0_27062023_JHIxs6o", // 0x00000da809fe7be1297ee731f998141ba352778d - PRICE_FEED_1INCH = "DEVX_PRICE_FEED_1INCH_V0_27062023_XhXA3sd", // 0x00000c451fa0b0a79a36c82820f061683e26714c - PRICE_FEED_TWT = "DEVX_PRICE_FEED_TWT_V0_27062023_92Xklvq", // 0x00000e862312c82af2301e6c433e75099665649d - PRICE_FEED_UNI = "DEVX_PRICE_FEED_UNI_V0_27062023_PBQ6vdq" // 0x0000095cce092e83e5826cfeb0f03cfa74915b41 -} */ - export enum DEPLOYMENT_SALTS { // PROD // 0x988C135a1049Ce61730724afD342fb7C56CD2776 : Deployer address ORACLE_AGGREGATOR = "PROD_CHAINLINK_ORACLE_AGGREGATOR_V0_27062023_UT8R11e", // 0x00000f7748595e46527413574a9327942e744e91 @@ -63,14 +44,7 @@ export enum DEPLOYMENT_SALTS { // PROD // when using deployer DEV // TOKEN_PAYMASTER = "DEVX_TOKEN_PAYMASTER_V0_08072023_h5AFKLa", // 0x0000023d6c240ae3c9610d519510004d2616c9ec - // V1.1.0 - SINGELTON_PAYMASTER = "PROD_SINGLETON_PAYMASTER_V1_06082023_II1mWTr", // 0x00000f79b7faf42eebadba19acc07cd08af44789 - - // 0xD3f89753278E419c8bda1eFe1366206B3D30C44f : Deployer address - - // V1.1.0 - // when using deployer DEV - // SINGELTON_PAYMASTER = "DEVX_SINGLETON_PAYMASTER_V1_03082023_0Af0vtw", // 0x0000064e9c653e373af18ef27f70be83df5476b7 + SPONSORSHIP_PAYMASTER = "PROD_SPONSORSHIP_PAYMASTER_V2", PRICE_FEED_USDC = "DEVX_PRICE_FEED_USDC_V0_27062023_uiaqdyv", // 0x000005abae3deadbe1fbd12105f950efba9eaec4 PRICE_FEED_USDT = "DEVX_PRICE_FEED_USDT_V0_27062023_dIos1Nw", // 0x000001e2c2b39542c30a3fe57c4487030bc03adf diff --git a/test/bundler-integration/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts b/test/bundler-integration/sponsorship-paymaster/biconomy-sponsorship-paymaster-specs.ts similarity index 82% rename from test/bundler-integration/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts rename to test/bundler-integration/sponsorship-paymaster/biconomy-sponsorship-paymaster-specs.ts index 6745375..8a2986f 100644 --- a/test/bundler-integration/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts +++ b/test/bundler-integration/sponsorship-paymaster/biconomy-sponsorship-paymaster-specs.ts @@ -8,34 +8,26 @@ import { BiconomyAccountImplementation__factory, BiconomyAccountFactory, BiconomyAccountFactory__factory, - VerifyingSingletonPaymasterV2, - VerifyingSingletonPaymasterV2__factory, + SponsorshipPaymaster, + SponsorshipPaymaster__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 { BigNumber, Signer } from "ethers"; import { BundlerTestEnvironment, EthSendUserOperationResult, UserOperationSubmissionError, } from "../environment/bundlerEnvironment"; -import { getUserOpEvent, parseEvent } from "../../utils/testUtils"; +import { parseEvent } from "../../utils/testUtils"; export const AddressZero = ethers.constants.AddressZero; @@ -55,19 +47,16 @@ export async function deployEntryPoint( 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 sponsorshipPaymaster: SponsorshipPaymaster; let smartWalletImp: BiconomyAccountImplementation; let ecdsaModule: EcdsaOwnershipRegistryModule; let walletFactory: BiconomyAccountFactory; - const abi = ethers.utils.defaultAbiCoder; let environment: BundlerTestEnvironment; @@ -81,11 +70,9 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { 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]; @@ -97,13 +84,14 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { deployer ).deploy(); - verifyingSingletonPaymaster = - await new VerifyingSingletonPaymasterV2__factory(deployer).deploy( - await deployer.getAddress(), - entryPoint.address, - offchainSignerAddress, - feeCollectorAddress - ); + sponsorshipPaymaster = await new SponsorshipPaymaster__factory( + deployer + ).deploy( + await deployer.getAddress(), + entryPoint.address, + offchainSignerAddress, + feeCollectorAddress + ); smartWalletImp = await new BiconomyAccountImplementation__factory( deployer @@ -139,14 +127,14 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { walletAddress = expected; console.log(" wallet address ", walletAddress); - paymasterAddress = verifyingSingletonPaymaster.address; + paymasterAddress = sponsorshipPaymaster.address; console.log("Paymaster address is ", paymasterAddress); await entryPoint .connect(deployer) .depositTo(paymasterAddress, { value: parseEther("1") }); - await verifyingSingletonPaymaster.connect(deployer).addStake(86400, { + await sponsorshipPaymaster.connect(deployer).addStake(86400, { value: parseEther("10"), }); }); @@ -177,7 +165,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { "0x" + "00".repeat(65), ]); - const res = await verifyingSingletonPaymaster.parsePaymasterAndData( + const res = await sponsorshipPaymaster.parsePaymasterAndData( paymasterAndData ); @@ -189,19 +177,17 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { }); it("succeed with valid signature", async () => { - const feeCollectorBalanceBefore = - await verifyingSingletonPaymaster.getBalance( - await feeCollector.getAddress() - ); + const feeCollectorBalanceBefore = await sponsorshipPaymaster.getBalance( + await feeCollector.getAddress() + ); expect(feeCollectorBalanceBefore).to.be.equal(BigNumber.from(0)); - const signer = await verifyingSingletonPaymaster.verifyingSigner(); + const signer = await sponsorshipPaymaster.verifyingSigner(); const offchainSignerAddress = await offchainSigner.getAddress(); expect(signer).to.be.equal(offchainSignerAddress); - await verifyingSingletonPaymaster.depositFor( - await offchainSigner.getAddress(), - { value: ethers.utils.parseEther("1") } - ); + await sponsorshipPaymaster.depositFor(await offchainSigner.getAddress(), { + value: ethers.utils.parseEther("1"), + }); const userOp1 = await fillAndSign( { sender: walletAddress, @@ -213,7 +199,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { "nonce" ); - const hash = await verifyingSingletonPaymaster.getHash( + const hash = await sponsorshipPaymaster.getHash( userOp1, await offchainSigner.getAddress(), MOCK_VALID_UNTIL, @@ -272,27 +258,24 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { entryPoint.handleOps([userOp], await offchainSigner.getAddress()) ).to.be.reverted; - const feeCollectorBalanceAfter = - await verifyingSingletonPaymaster.getBalance( - await feeCollector.getAddress() - ); + const feeCollectorBalanceAfter = await sponsorshipPaymaster.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() - ); + const feeCollectorBalanceBefore = await sponsorshipPaymaster.getBalance( + await feeCollector.getAddress() + ); expect(feeCollectorBalanceBefore).to.be.equal(BigNumber.from(0)); - const signer = await verifyingSingletonPaymaster.verifyingSigner(); + const signer = await sponsorshipPaymaster.verifyingSigner(); const offchainSignerAddress = await offchainSigner.getAddress(); expect(signer).to.be.equal(offchainSignerAddress); - await verifyingSingletonPaymaster.depositFor( - await offchainSigner.getAddress(), - { value: ethers.utils.parseEther("1") } - ); + await sponsorshipPaymaster.depositFor(await offchainSigner.getAddress(), { + value: ethers.utils.parseEther("1"), + }); const userOp1 = await fillAndSign( { sender: walletAddress, @@ -304,7 +287,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { "nonce" ); - const hash = await verifyingSingletonPaymaster.getHash( + const hash = await sponsorshipPaymaster.getHash( userOp1, await offchainSigner.getAddress(), MOCK_VALID_UNTIL, @@ -363,19 +346,17 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { }); it("fails if preVerificationGas is not enough", async () => { - const feeCollectorBalanceBefore = - await verifyingSingletonPaymaster.getBalance( - await feeCollector.getAddress() - ); + const feeCollectorBalanceBefore = await sponsorshipPaymaster.getBalance( + await feeCollector.getAddress() + ); expect(feeCollectorBalanceBefore).to.be.equal(BigNumber.from(0)); - const signer = await verifyingSingletonPaymaster.verifyingSigner(); + const signer = await sponsorshipPaymaster.verifyingSigner(); const offchainSignerAddress = await offchainSigner.getAddress(); expect(signer).to.be.equal(offchainSignerAddress); - await verifyingSingletonPaymaster.depositFor( - await offchainSigner.getAddress(), - { value: ethers.utils.parseEther("1") } - ); + await sponsorshipPaymaster.depositFor(await offchainSigner.getAddress(), { + value: ethers.utils.parseEther("1"), + }); const userOp1 = await fillAndSign( { sender: walletAddress, @@ -387,7 +368,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { "nonce" ); - const hash = await verifyingSingletonPaymaster.getHash( + const hash = await sponsorshipPaymaster.getHash( userOp1, await offchainSigner.getAddress(), MOCK_VALID_UNTIL, diff --git a/test/bundler-integration/token-paymaster/btpm-undeployed-wallet.ts b/test/bundler-integration/token-paymaster/btpm-undeployed-wallet.ts index 1d9f8db..256cc7c 100644 --- a/test/bundler-integration/token-paymaster/btpm-undeployed-wallet.ts +++ b/test/bundler-integration/token-paymaster/btpm-undeployed-wallet.ts @@ -31,7 +31,10 @@ import { } from "../../../lib/account-abstraction/typechain"; import { arrayify, hexConcat, parseEther } from "ethers/lib/utils"; import { BigNumber, BigNumberish, Contract, Signer } from "ethers"; -import { BundlerTestEnvironment, EthSendUserOperationResult } from "../environment/bundlerEnvironment"; +import { + BundlerTestEnvironment, + EthSendUserOperationResult, +} from "../environment/bundlerEnvironment"; import { getUserOpEvent, parseEvent } from "../../utils/testUtils"; export const AddressZero = ethers.constants.AddressZero; diff --git a/test/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts b/test/sponsorship-paymaster/biconomy-sponsorship-paymaster-specs.ts similarity index 81% rename from test/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts rename to test/sponsorship-paymaster/biconomy-sponsorship-paymaster-specs.ts index dfc541d..3f7f414 100644 --- a/test/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts +++ b/test/sponsorship-paymaster/biconomy-sponsorship-paymaster-specs.ts @@ -8,28 +8,20 @@ import { BiconomyAccountImplementation__factory, BiconomyAccountFactory, BiconomyAccountFactory__factory, - VerifyingSingletonPaymasterV2, - VerifyingSingletonPaymasterV2__factory, + SponsorshipPaymaster, + SponsorshipPaymaster__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 { BigNumber, Signer } from "ethers"; export const AddressZero = ethers.constants.AddressZero; @@ -46,7 +38,6 @@ export async function deployEntryPoint( describe("EntryPoint with VerifyingPaymaster Singleton", function () { let entryPoint: EntryPoint; - let entryPointStatic: EntryPoint; let depositorSigner: Signer; let walletOwner: Signer; let walletAddress: string, paymasterAddress: string; @@ -54,7 +45,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { let offchainSigner: Signer, deployer: Signer, feeCollector: Signer; - let verifyingSingletonPaymaster: VerifyingSingletonPaymasterV2; + let sponsorshipPaymaster: SponsorshipPaymaster; let smartWalletImp: BiconomyAccountImplementation; let ecdsaModule: EcdsaOwnershipRegistryModule; let walletFactory: BiconomyAccountFactory; @@ -63,7 +54,6 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { beforeEach(async function () { ethersSigner = await ethers.getSigners(); entryPoint = await deployEntryPoint(); - entryPointStatic = entryPoint.connect(AddressZero); deployer = ethersSigner[0]; offchainSigner = ethersSigner[1]; @@ -79,13 +69,14 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { deployer ).deploy(); - verifyingSingletonPaymaster = - await new VerifyingSingletonPaymasterV2__factory(deployer).deploy( - await deployer.getAddress(), - entryPoint.address, - offchainSignerAddress, - feeCollectorAddress - ); + sponsorshipPaymaster = await new SponsorshipPaymaster__factory( + deployer + ).deploy( + await deployer.getAddress(), + entryPoint.address, + offchainSignerAddress, + feeCollectorAddress + ); smartWalletImp = await new BiconomyAccountImplementation__factory( deployer @@ -121,7 +112,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { walletAddress = expected; console.log(" wallet address ", walletAddress); - paymasterAddress = verifyingSingletonPaymaster.address; + paymasterAddress = sponsorshipPaymaster.address; console.log("Paymaster address is ", paymasterAddress); await entryPoint.depositTo(paymasterAddress, { value: parseEther("1") }); @@ -137,7 +128,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { "nonce" ); - const hash = await verifyingSingletonPaymaster.getHash( + const hash = await sponsorshipPaymaster.getHash( userOp1, paymasterId, MOCK_VALID_UNTIL, @@ -175,24 +166,24 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { it("succeed with valid signature", async () => { const fundingId = await offchainSigner.getAddress(); - await verifyingSingletonPaymaster + await sponsorshipPaymaster .connect(deployer) .setUnaccountedEPGasOverhead(24000); - await verifyingSingletonPaymaster.depositFor(fundingId, { + await sponsorshipPaymaster.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() - ); + const paymasterIdBalanceBefore = await sponsorshipPaymaster.getBalance( + fundingId + ); + const feeCollectorBalanceBefore = await sponsorshipPaymaster.getBalance( + await feeCollector.getAddress() + ); console.log("feeCollectorBalanceBefore ", feeCollectorBalanceBefore); expect(feeCollectorBalanceBefore).to.be.equal(BigNumber.from(0)); - const signer = await verifyingSingletonPaymaster.verifyingSigner(); + const signer = await sponsorshipPaymaster.verifyingSigner(); const offchainSignerAddress = await offchainSigner.getAddress(); expect(signer).to.be.equal(offchainSignerAddress); @@ -206,7 +197,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { "nonce" ); - const hash = await verifyingSingletonPaymaster.getHash( + const hash = await sponsorshipPaymaster.getHash( userOp1, fundingId, MOCK_VALID_UNTIL, @@ -249,8 +240,9 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { console.log("bundler paid ", bundlerPaid.toString()); const paymasterFundsAfter = await entryPoint.balanceOf(paymasterAddress); - const paymasterIdBalanceAfter = - await verifyingSingletonPaymaster.getBalance(fundingId); + const paymasterIdBalanceAfter = await sponsorshipPaymaster.getBalance( + fundingId + ); const paymasterIdBalanceDiff = paymasterIdBalanceBefore.sub( paymasterIdBalanceAfter @@ -279,10 +271,9 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { entryPoint.handleOps([userOp], await offchainSigner.getAddress()) ).to.be.reverted; - const feeCollectorBalanceAfter = - await verifyingSingletonPaymaster.getBalance( - await feeCollector.getAddress() - ); + const feeCollectorBalanceAfter = await sponsorshipPaymaster.getBalance( + await feeCollector.getAddress() + ); expect(feeCollectorBalanceAfter).to.be.greaterThan(BigNumber.from(0)); // 0.1 / 1.1 = actual gas used * 0.1 @@ -308,7 +299,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { "0x" + "00".repeat(65), ]); - const res = await verifyingSingletonPaymaster.parsePaymasterAndData( + const res = await sponsorshipPaymaster.parsePaymasterAndData( paymasterAndData ); @@ -323,18 +314,16 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { const paymasterAndData = "0x9c145aed00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000124468721a7000000000000000000000000831153c6b9537d0ff5b7db830c2749de3042e77600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000649b890b7000000000000000000000000071bc333f52a7971dde5de5b6c1ab0e7341e9724c00000000000000000000000021a6f9fa7246de45762e6f9db1f55e3c0f8566db00000000000000000000000000000000000000000000000000000000000000"; - await expect( - verifyingSingletonPaymaster.parsePaymasterAndData(paymasterAndData) - ).to.be.reverted; + await expect(sponsorshipPaymaster.parsePaymasterAndData(paymasterAndData)) + .to.be.reverted; }); it("should check the correct states set on the paymaster", async () => { - const owner = await verifyingSingletonPaymaster.owner(); + const owner = await sponsorshipPaymaster.owner(); - const verifyingSigner = - await verifyingSingletonPaymaster.verifyingSigner(); + const verifyingSigner = await sponsorshipPaymaster.verifyingSigner(); - const feeReceiver = await verifyingSingletonPaymaster.feeCollector(); + const feeReceiver = await sponsorshipPaymaster.feeCollector(); expect(owner).to.be.equal(deployer.address); expect(verifyingSigner).to.be.equal(offchainSigner.address); @@ -346,11 +335,11 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { it("Deposits into the specified address", async () => { const paymasterId = await depositorSigner.getAddress(); - await verifyingSingletonPaymaster.depositFor(paymasterId, { + await sponsorshipPaymaster.depositFor(paymasterId, { value: parseEther("1"), }); - const balance = await verifyingSingletonPaymaster.getBalance(paymasterId); + const balance = await sponsorshipPaymaster.getBalance(paymasterId); expect(balance).to.be.equal(parseEther("1")); }); @@ -358,11 +347,11 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { const paymasterId = await depositorSigner.getAddress(); await expect( - verifyingSingletonPaymaster.depositFor(paymasterId, { + sponsorshipPaymaster.depositFor(paymasterId, { value: parseEther("0"), }) ).to.be.revertedWithCustomError( - verifyingSingletonPaymaster, + sponsorshipPaymaster, "DepositCanNotBeZero" ); }); @@ -371,11 +360,11 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { const paymasterId = ethers.constants.AddressZero; await expect( - verifyingSingletonPaymaster.depositFor(paymasterId, { + sponsorshipPaymaster.depositFor(paymasterId, { value: parseEther("0.5"), }) ).to.be.revertedWithCustomError( - verifyingSingletonPaymaster, + sponsorshipPaymaster, "PaymasterIdCannotBeZero" ); }); @@ -384,11 +373,11 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () { const paymasterId = await depositorSigner.getAddress(); await expect( - verifyingSingletonPaymaster + sponsorshipPaymaster .connect(depositorSigner) .withdrawTo(paymasterId, parseEther("1.1")) ).to.be.revertedWithCustomError( - verifyingSingletonPaymaster, + sponsorshipPaymaster, "InsufficientBalance" ); }); diff --git a/test/sponsorship-paymaster/biconomy-verifying-paymaster-specs.ts b/test/sponsorship-paymaster/biconomy-verifying-paymaster-specs.ts deleted file mode 100644 index fb4bc67..0000000 --- a/test/sponsorship-paymaster/biconomy-verifying-paymaster-specs.ts +++ /dev/null @@ -1,243 +0,0 @@ -/* eslint-disable node/no-missing-import */ -/* eslint-disable camelcase */ -import { expect } from "chai"; -import { ethers } from "hardhat"; - -import { - BiconomyAccountImplementation, - BiconomyAccountImplementation__factory, - BiconomyAccountFactory, - BiconomyAccountFactory__factory, - VerifyingSingletonPaymaster, - VerifyingSingletonPaymaster__factory, -} from "../../typechain-types"; -// Review: Could import from scw-contracts submodules to be consistent -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"; - -export const AddressZero = ethers.constants.AddressZero; - -const MOCK_VALID_UNTIL = "0x00000000deadbeef"; -const MOCK_VALID_AFTER = "0x0000000000001234"; - -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 proxyPaymaster: Contract; - let walletAddress: string, paymasterAddress: string; - let ethersSigner; - - let offchainSigner: Signer, deployer: Signer; - - let verifyingSingletonPaymaster: VerifyingSingletonPaymaster; - // Could also use published package or added submodule (for Account Implementation and Factory) - let smartWalletImp: BiconomyAccountImplementation; - let ecdsaModule: EcdsaOwnershipRegistryModule; - let walletFactory: BiconomyAccountFactory; - const abi = ethers.utils.defaultAbiCoder; - - beforeEach(async function () { - ethersSigner = await ethers.getSigners(); - entryPoint = await deployEntryPoint(); - entryPointStatic = entryPoint.connect(AddressZero); - - deployer = ethersSigner[0]; - offchainSigner = ethersSigner[1]; - depositorSigner = ethersSigner[2]; - walletOwner = deployer; // ethersSigner[0]; - - const offchainSignerAddress = await offchainSigner.getAddress(); - const walletOwnerAddress = await walletOwner.getAddress(); - - ecdsaModule = await new EcdsaOwnershipRegistryModule__factory( - deployer - ).deploy(); - - verifyingSingletonPaymaster = - await new VerifyingSingletonPaymaster__factory(deployer).deploy( - await deployer.getAddress(), - entryPoint.address, - offchainSignerAddress - ); - - 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; - - paymasterAddress = verifyingSingletonPaymaster.address; - - await verifyingSingletonPaymaster - .connect(deployer) - .addStake(86400, { value: parseEther("2") }); - - await entryPoint.depositTo(paymasterAddress, { value: parseEther("1") }); - }); - - async function getUserOpWithPaymasterInfo(paymasterId: string) { - const userOp1 = await fillAndSign( - { - sender: walletAddress, - }, - walletOwner, - entryPoint, - "nonce" - ); - - const hash = await verifyingSingletonPaymaster.getHash( - userOp1, - paymasterId, - MOCK_VALID_UNTIL, - MOCK_VALID_AFTER - ); - const sig = await offchainSigner.signMessage(arrayify(hash)); - const paymasterData = abi.encode( - ["address", "uint48", "uint48", "bytes"], - [paymasterId, MOCK_VALID_UNTIL, MOCK_VALID_AFTER, sig] - ); - const paymasterAndData = hexConcat([paymasterAddress, paymasterData]); - return await fillAndSign( - { - ...userOp1, - paymasterAndData, - }, - walletOwner, - entryPoint, - "nonce" - ); - } - - describe("#validatePaymasterUserOp", () => { - it("Should Fail when there is no deposit for paymaster id", async () => { - const paymasterId = await depositorSigner.getAddress(); - const userOp = await getUserOpWithPaymasterInfo(paymasterId); - - const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode( - ["bytes", "address"], - [userOp.signature, ecdsaModule.address] - ); - - userOp.signature = signatureWithModuleAddress; - - await expect( - entryPoint.callStatic.simulateValidation(userOp) - // ).to.be.revertedWith("FailedOp"); - ).to.be.reverted; - }); - - it("succeed with valid signature", async () => { - 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, - }, - walletOwner, - entryPoint, - "nonce" - ); - - const hash = await verifyingSingletonPaymaster.getHash( - userOp1, - await offchainSigner.getAddress(), - MOCK_VALID_UNTIL, - MOCK_VALID_AFTER - ); - const sig = await offchainSigner.signMessage(arrayify(hash)); - const userOp = await fillAndSign( - { - ...userOp1, - paymasterAndData: hexConcat([ - paymasterAddress, - ethers.utils.defaultAbiCoder.encode( - ["address", "uint48", "uint48", "bytes"], - [ - await offchainSigner.getAddress(), - MOCK_VALID_UNTIL, - MOCK_VALID_AFTER, - sig, - ] - ), - ]), - }, - walletOwner, - entryPoint, - "nonce" - ); - - const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode( - ["bytes", "address"], - [userOp.signature, ecdsaModule.address] - ); - - userOp.signature = signatureWithModuleAddress; - - await entryPoint.handleOps([userOp], await offchainSigner.getAddress()); - // gas used VPM V1 134369 - await expect( - entryPoint.handleOps([userOp], await offchainSigner.getAddress()) - ).to.be.reverted; - }); - }); -});