diff --git a/contracts/core/BasePaymaster.sol b/contracts/core/BasePaymaster.sol index efb8b9cbe..916bae304 100644 --- a/contracts/core/BasePaymaster.sol +++ b/contracts/core/BasePaymaster.sol @@ -58,10 +58,11 @@ abstract contract BasePaymaster is IPaymaster, Ownable { function postOp( PostOpMode mode, bytes calldata context, - uint256 actualGasCost + uint256 actualGasCost, + uint actualUserOpFeePerGas ) external override { _requireFromEntryPoint(); - _postOp(mode, context, actualGasCost); + _postOp(mode, context, actualGasCost, actualUserOpFeePerGas); } /** @@ -75,14 +76,18 @@ abstract contract BasePaymaster is IPaymaster, Ownable { * postOpReverted - User op succeeded, but caused postOp (in mode=opSucceeded) to revert. * Now this is the 2nd call, after user's op was deliberately reverted. * @param context - The context value returned by validatePaymasterUserOp - * @param actualGasCost - Actual gas used so far (without this postOp call). + * @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas + * and maxPriorityFee (and basefee) + * It is not the same as tx.gasprice, which is what the bundler pays. + */ function _postOp( PostOpMode mode, bytes calldata context, - uint256 actualGasCost + uint256 actualGasCost, + uint actualUserOpFeePerGas ) internal virtual { - (mode, context, actualGasCost); // unused params + (mode, context, actualGasCost, actualUserOpFeePerGas); // unused params // subclass must override this method if validatePaymasterUserOp returns a context revert("must override"); } diff --git a/contracts/core/EntryPoint.sol b/contracts/core/EntryPoint.sol index 0a3994bb6..b0b9f9e9a 100644 --- a/contracts/core/EntryPoint.sol +++ b/contracts/core/EntryPoint.sol @@ -665,7 +665,7 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard, if (mode != IPaymaster.PostOpMode.postOpReverted) { try IPaymaster(paymaster).postOp{ gas: mUserOp.paymasterPostOpGasLimit - }(mode, context, actualGasCost) + }(mode, context, actualGasCost, gasPrice) // solhint-disable-next-line no-empty-blocks {} catch { bytes memory reason = Exec.getReturnData(REVERT_REASON_MAX_LEN); diff --git a/contracts/interfaces/IPaymaster.sol b/contracts/interfaces/IPaymaster.sol index 65f69093a..a8bd1b836 100644 --- a/contracts/interfaces/IPaymaster.sol +++ b/contracts/interfaces/IPaymaster.sol @@ -52,10 +52,14 @@ interface IPaymaster { * Now this is the 2nd call, after user's op was deliberately reverted. * @param context - The context value returned by validatePaymasterUserOp * @param actualGasCost - Actual gas used so far (without this postOp call). + * @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas + * and maxPriorityFee (and basefee) + * It is not the same as tx.gasprice, which is what the bundler pays. */ function postOp( PostOpMode mode, bytes calldata context, - uint256 actualGasCost + uint256 actualGasCost, + uint256 actualUserOpFeePerGas ) external; } diff --git a/contracts/samples/LegacyTokenPaymaster.sol b/contracts/samples/LegacyTokenPaymaster.sol index d673aefcb..46819cc47 100644 --- a/contracts/samples/LegacyTokenPaymaster.sol +++ b/contracts/samples/LegacyTokenPaymaster.sol @@ -103,11 +103,11 @@ contract LegacyTokenPaymaster is BasePaymaster, ERC20 { * the user's TX , back to the state it was before the transaction started (before the validatePaymasterUserOp), * and the transaction should succeed there. */ - function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal override { + function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost, uint actualUserOpFeePerGas) internal override { //we don't really care about the mode, we just pay the gas with the user's tokens. (mode); address sender = abi.decode(context, (address)); - uint256 charge = getTokenValueOfEth(actualGasCost + COST_OF_POST); + uint256 charge = getTokenValueOfEth(actualGasCost + COST_OF_POST * actualUserOpFeePerGas); //actualGasCost is known to be no larger than the above requiredPreFund, so the transfer should succeed. _transfer(sender, address(this), charge); } diff --git a/contracts/samples/TokenPaymaster.sol b/contracts/samples/TokenPaymaster.sol index 00238e1e2..02bfbea3d 100644 --- a/contracts/samples/TokenPaymaster.sol +++ b/contracts/samples/TokenPaymaster.sol @@ -137,7 +137,7 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper { } uint256 tokenAmount = weiToToken(preChargeNative, cachedPriceWithMarkup); SafeERC20.safeTransferFrom(token, userOp.sender, address(this), tokenAmount); - context = abi.encode(tokenAmount, userOp.maxFeePerGas, userOp.maxPriorityFeePerGas, userOp.sender); + context = abi.encode(tokenAmount, userOp.sender); validationResult = _packValidationData( false, uint48(cachedPriceTimestamp + tokenPaymasterConfig.priceMaxAge), @@ -150,21 +150,21 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper { /// @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) internal override { + /// @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas + // and maxPriorityFee (and basefee) + // It is not the same as tx.gasprice, which is what the bundler pays. + function _postOp(PostOpMode, bytes calldata context, uint256 actualGasCost, uint actualUserOpFeePerGas) internal override { unchecked { uint256 priceMarkup = tokenPaymasterConfig.priceMarkup; ( uint256 preCharge, - uint256 maxFeePerGas, - uint256 maxPriorityFeePerGas, address userOpSender - ) = abi.decode(context, (uint256, uint256, uint256, address)); - uint256 gasPrice = getGasPrice(maxFeePerGas, maxPriorityFeePerGas); + ) = abi.decode(context, (uint256, address)); uint256 _cachedPrice = updateCachedPrice(false); // note: as price is in ether-per-token and we want more tokens increasing it means dividing it by markup uint256 cachedPriceWithMarkup = _cachedPrice * PRICE_DENOMINATOR / priceMarkup; // Refund tokens based on actual gas cost - uint256 actualChargeNative = actualGasCost + tokenPaymasterConfig.refundPostopCost * gasPrice; + uint256 actualChargeNative = actualGasCost + tokenPaymasterConfig.refundPostopCost * actualUserOpFeePerGas; uint256 actualTokenNeeded = weiToToken(actualChargeNative, cachedPriceWithMarkup); if (preCharge > actualTokenNeeded) { // If the initially provided token amount is greater than the actual amount needed, refund the difference diff --git a/contracts/test/TestPaymasterRevertCustomError.sol b/contracts/test/TestPaymasterRevertCustomError.sol index 6af71bf46..ccddb65d7 100644 --- a/contracts/test/TestPaymasterRevertCustomError.sol +++ b/contracts/test/TestPaymasterRevertCustomError.sol @@ -33,7 +33,7 @@ contract TestPaymasterRevertCustomError is BasePaymaster { revertType = _revertType; } - function _postOp(PostOpMode, bytes calldata, uint256) internal view override { + function _postOp(PostOpMode, bytes calldata, uint256, uint256) internal view override { if (revertType == RevertType.customError){ revert CustomError("this is a long revert reason string we are looking for"); } diff --git a/contracts/test/TestPaymasterWithPostOp.sol b/contracts/test/TestPaymasterWithPostOp.sol index 9590d5130..f43038999 100644 --- a/contracts/test/TestPaymasterWithPostOp.sol +++ b/contracts/test/TestPaymasterWithPostOp.sol @@ -13,18 +13,14 @@ contract TestPaymasterWithPostOp is TestPaymasterAcceptAll { constructor(IEntryPoint _entryPoint) TestPaymasterAcceptAll(_entryPoint) { } - function _validatePaymasterUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) + function _validatePaymasterUserOp(PackedUserOperation calldata, bytes32, uint256) internal virtual override view returns (bytes memory context, uint256 validationData) { - (userOp, userOpHash, maxCost); // return a context, to force a call for postOp. return ("1", 0); } - function _postOp( - PostOpMode mode, - bytes calldata context, - uint256 actualGasCost - ) internal override { + function _postOp(PostOpMode, bytes calldata, uint256, uint256) + internal override { } } diff --git a/erc/ERCS/erc-4337.md b/erc/ERCS/erc-4337.md index 9d4a3f7ea..e765508f1 100644 --- a/erc/ERCS/erc-4337.md +++ b/erc/ERCS/erc-4337.md @@ -263,7 +263,7 @@ The paymaster interface is as follows: external returns (bytes memory context, uint256 validationData); function postOp - (PostOpMode mode, bytes calldata context, uint256 actualGasCost) + (PostOpMode mode, bytes calldata context, uint256 actualGasCost, uint256 actualUserOpFeePerGas) external; enum PostOpMode { diff --git a/reports/gas-checker.txt b/reports/gas-checker.txt index e09f02d9e..d1177bcfc 100644 --- a/reports/gas-checker.txt +++ b/reports/gas-checker.txt @@ -16,40 +16,40 @@ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ ║ simple - diff from previous │ 2 │ │ 44884 │ 15905 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple │ 10 │ 486740 │ │ ║ +║ simple │ 10 │ 486728 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple - diff from previous │ 11 │ │ 44929 │ 15950 ║ +║ simple - diff from previous │ 11 │ │ 44881 │ 15902 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster │ 1 │ 89079 │ │ ║ +║ simple paymaster │ 1 │ 89067 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster with diff │ 2 │ │ 43987 │ 15008 ║ +║ simple paymaster with diff │ 2 │ │ 43975 │ 14996 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster │ 10 │ 485083 │ │ ║ +║ simple paymaster │ 10 │ 484963 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster with diff │ 11 │ │ 43981 │ 15002 ║ +║ simple paymaster with diff │ 11 │ │ 44029 │ 15050 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ big tx 5k │ 1 │ 183735 │ │ ║ +║ big tx 5k │ 1 │ 183723 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ big tx - diff from previous │ 2 │ │ 145383 │ 20159 ║ +║ big tx - diff from previous │ 2 │ │ 145395 │ 20171 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ big tx 5k │ 10 │ 1492351 │ │ ║ +║ big tx 5k │ 10 │ 1492327 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ big tx - diff from previous │ 11 │ │ 145452 │ 20228 ║ +║ big tx - diff from previous │ 11 │ │ 145512 │ 20288 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ paymaster+postOp │ 1 │ 90919 │ │ ║ +║ paymaster+postOp │ 1 │ 90943 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ paymaster+postOp with diff │ 2 │ │ 45802 │ 16823 ║ +║ paymaster+postOp with diff │ 2 │ │ 45838 │ 16859 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ paymaster+postOp │ 10 │ 503479 │ │ ║ +║ paymaster+postOp │ 10 │ 503743 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ paymaster+postOp with diff │ 11 │ │ 45875 │ 16896 ║ +║ paymaster+postOp with diff │ 11 │ │ 45887 │ 16908 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ token paymaster │ 1 │ 149447 │ │ ║ +║ token paymaster │ 1 │ 148703 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ token paymaster with diff │ 2 │ │ 74046 │ 45067 ║ +║ token paymaster with diff │ 2 │ │ 73307 │ 44328 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ token paymaster │ 10 │ 816174 │ │ ║ +║ token paymaster │ 10 │ 808665 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ token paymaster with diff │ 11 │ │ 74106 │ 45127 ║ +║ token paymaster with diff │ 11 │ │ 73378 │ 44399 ║ ╚════════════════════════════════╧═══════╧═══════════════╧════════════════╧═════════════════════╝