From 81abba53782efbd1111888b0fc1deeb0df85aebf Mon Sep 17 00:00:00 2001 From: Shivaansh Kapoor Date: Tue, 9 Jul 2024 16:48:47 +0400 Subject: [PATCH] postop cost added back and more tests added --- contracts/common/Errors.sol | 5 +++ .../SponsorshipPaymasterWithPremium.sol | 28 +++++++++++- test/foundry/base/NexusTestBase.sol | 4 +- ...tSponsorshipPaymasterWithPremiumTest.t.sol | 43 +++++++++++++++++++ 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/contracts/common/Errors.sol b/contracts/common/Errors.sol index 58bf40f..c72ea2e 100644 --- a/contracts/common/Errors.sol +++ b/contracts/common/Errors.sol @@ -66,4 +66,9 @@ contract BiconomySponsorshipPaymasterErrors { * @notice Throws when trying to withdraw to address(0) */ error CanNotWithdrawToZeroAddress(); + + /** + * @notice Throws when trying postOpCost is too high + */ + error PostOpCostTooHigh(); } diff --git a/contracts/sponsorship/SponsorshipPaymasterWithPremium.sol b/contracts/sponsorship/SponsorshipPaymasterWithPremium.sol index e63b539..be63de2 100644 --- a/contracts/sponsorship/SponsorshipPaymasterWithPremium.sol +++ b/contracts/sponsorship/SponsorshipPaymasterWithPremium.sol @@ -39,6 +39,7 @@ contract BiconomySponsorshipPaymaster is address public verifyingSigner; address public feeCollector; + uint48 public postOpCost; uint32 private constant PRICE_DENOMINATOR = 1e6; // note: could rename to PAYMASTER_ID_OFFSET @@ -119,6 +120,20 @@ contract BiconomySponsorshipPaymaster is emit FeeCollectorChanged(oldFeeCollector, _newFeeCollector, msg.sender); } + /** + * @dev Set a new unaccountedEPGasOverhead value. + * @param value The new value to be set as the unaccountedEPGasOverhead. + * @notice only to be called by the owner of the contract. + */ + function setPostopCost(uint48 value) external payable onlyOwner { + if (value > 200_000) { + revert PostOpCostTooHigh(); + } + uint256 oldValue = postOpCost; + postOpCost = value; + emit PostopCostChanged(oldValue, value); + } + /** * @dev Override the default implementation. */ @@ -232,12 +247,21 @@ contract BiconomySponsorshipPaymaster is /// @dev This function is called after a user operation has been executed or reverted. /// @param context The context containing the token amount and user sender address. /// @param actualGasCost The actual gas cost of the transaction. - function _postOp(PostOpMode, bytes calldata context, uint256 actualGasCost, uint256) internal override { + function _postOp( + PostOpMode, + bytes calldata context, + uint256 actualGasCost, + uint256 actualUserOpFeePerGas + ) + internal + override + { unchecked { (address paymasterId, uint32 dynamicAdjustment, bytes32 userOpHash) = abi.decode(context, (address, uint32, bytes32)); - uint256 adjustedGasCost = (actualGasCost * dynamicAdjustment) / PRICE_DENOMINATOR; + uint256 totalGasCost = actualGasCost + (postOpCost * actualUserOpFeePerGas); + uint256 adjustedGasCost = (totalGasCost * dynamicAdjustment) / PRICE_DENOMINATOR; // Deduct the adjusted cost paymasterIdBalances[paymasterId] -= adjustedGasCost; diff --git a/test/foundry/base/NexusTestBase.sol b/test/foundry/base/NexusTestBase.sol index dbe176c..f94712f 100644 --- a/test/foundry/base/NexusTestBase.sol +++ b/test/foundry/base/NexusTestBase.sol @@ -477,6 +477,8 @@ abstract contract NexusTestBase is CheatCodes, BaseEventsAndErrors { //premium expectedPremium = totalGasFeesCharged - ((totalGasFeesCharged * 1e6) / premium); actualPremium = resultingFeeCollectorPaymasterBalance - initialFeeCollectorBalance; - } else revert("Premium must be more than 1e6"); + } else { + revert("Premium must be more than 1e6"); + } } } diff --git a/test/foundry/unit/concrete/TestSponsorshipPaymasterWithPremiumTest.t.sol b/test/foundry/unit/concrete/TestSponsorshipPaymasterWithPremiumTest.t.sol index a1df8d2..18853c9 100644 --- a/test/foundry/unit/concrete/TestSponsorshipPaymasterWithPremiumTest.t.sol +++ b/test/foundry/unit/concrete/TestSponsorshipPaymasterWithPremiumTest.t.sol @@ -106,6 +106,24 @@ contract TestSponsorshipPaymasterWithPremium is NexusTestBase { bicoPaymaster.setFeeCollector(DAN_ADDRESS); } + function test_SetPostopCost() external prankModifier(PAYMASTER_OWNER.addr) { + uint48 initialPostopCost = bicoPaymaster.postOpCost(); + uint48 newPostopCost = 5_000; + + vm.expectEmit(true, true, false, true, address(bicoPaymaster)); + emit IBiconomySponsorshipPaymaster.PostopCostChanged(initialPostopCost, newPostopCost); + bicoPaymaster.setPostopCost(newPostopCost); + + uint48 resultingPostopCost = bicoPaymaster.postOpCost(); + assertEq(resultingPostopCost, newPostopCost); + } + + function test_RevertIf_SetPostopCostToHigh() external prankModifier(PAYMASTER_OWNER.addr) { + uint48 newPostopCost = 200_001; + vm.expectRevert(abi.encodeWithSelector(PostOpCostTooHigh.selector)); + bicoPaymaster.setPostopCost(newPostopCost); + } + function test_DepositFor() external { uint256 dappPaymasterBalance = bicoPaymaster.getBalance(DAPP_ACCOUNT.addr); uint256 depositAmount = 10 ether; @@ -134,6 +152,31 @@ contract TestSponsorshipPaymasterWithPremium is NexusTestBase { bicoPaymaster.deposit{ value: 1 ether }(); } + function test_WithdrawTo() external prankModifier(DAPP_ACCOUNT.addr) { + uint256 depositAmount = 10 ether; + bicoPaymaster.depositFor{ value: depositAmount }(DAPP_ACCOUNT.addr); + uint256 danInitialBalance = DAN_ADDRESS.balance; + + vm.expectEmit(true, true, true, true, address(bicoPaymaster)); + emit IBiconomySponsorshipPaymaster.GasWithdrawn(DAPP_ACCOUNT.addr, DAN_ADDRESS, depositAmount); + bicoPaymaster.withdrawTo(payable(DAN_ADDRESS), depositAmount); + + uint256 dappPaymasterBalance = bicoPaymaster.getBalance(DAPP_ACCOUNT.addr); + assertEq(dappPaymasterBalance, 0 ether); + uint256 expectedDanBalance = danInitialBalance + depositAmount; + assertEq(DAN_ADDRESS.balance, expectedDanBalance); + } + + function test_RevertIf_WithdrawToZeroAddress() external prankModifier(DAPP_ACCOUNT.addr) { + vm.expectRevert(abi.encodeWithSelector(CanNotWithdrawToZeroAddress.selector)); + bicoPaymaster.withdrawTo(payable(address(0)), 0 ether); + } + + function test_RevertIf_WithdrawToExceedsBalance() external prankModifier(DAPP_ACCOUNT.addr) { + vm.expectRevert(abi.encodeWithSelector(InsufficientFundsInGasTank.selector)); + bicoPaymaster.withdrawTo(payable(DAN_ADDRESS), 1 ether); + } + function test_ValidatePaymasterAndPostOpWithoutPremium() external prankModifier(DAPP_ACCOUNT.addr) { bicoPaymaster.depositFor{ value: 10 ether }(DAPP_ACCOUNT.addr); // No premoium