From e6187602722c756697fd4432559f3e7e9847b157 Mon Sep 17 00:00:00 2001 From: Jordan Hagan Date: Mon, 26 Feb 2024 09:59:21 +1300 Subject: [PATCH 1/5] recovered stakeshare from history --- .../FastLaneAuctionHandler.sol | 64 +++++++++++++++++-- .../interfaces/IFastLaneAuctionHandler.sol | 2 + 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/contracts/auction-handler/FastLaneAuctionHandler.sol b/contracts/auction-handler/FastLaneAuctionHandler.sol index 38e1aae..5061bf3 100644 --- a/contracts/auction-handler/FastLaneAuctionHandler.sol +++ b/contracts/auction-handler/FastLaneAuctionHandler.sol @@ -45,6 +45,7 @@ abstract contract FastLaneAuctionHandlerEvents { event RelayWithdrawStuckNativeToken(address indexed receiver, uint256 amount); event RelayProcessingPaidValidator(address indexed validator, uint256 validatorPayment, address indexed initiator); + event RelayProcessingWithdrewStakeShare(address indexed recipient, uint256 amountWithdrawn); event RelayFeeCollected(address indexed payor, address indexed payee, uint256 amount); @@ -99,6 +100,7 @@ abstract contract FastLaneAuctionHandlerEvents { error RelayCustomPayoutCantBePartial(); error RelayUnapprovedReentrancy(); + error RelayOnlyStakeShareRecipient(); } /// @notice Validator Data Struct @@ -154,6 +156,11 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { /// @notice Map[validator] = % payment to validator in a bid with refund mapping(address => int256) private validatorsRefundShareMap; + + uint24 internal constant FEE_BASE = 1_000_000; + uint24 internal constant flStakeShareRatio = 50_000; // 5% + address internal constant StakeShareRecipient = address(0x01); + uint256 public flStakeSharePayable; bytes32 private constant UNLOCKED = bytes32(uint256(1)); bytes32 private constant LOCKED = bytes32(uint256(2)); @@ -431,8 +438,11 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { _bidAmount = address(this).balance - balanceBefore; } - validatorsBalanceMap[block.coinbase] += _bidAmount; - validatorsTotal += _bidAmount; + (uint256 amtPayableToValidator, uint256 amtPayableToStakers) = _calculateStakeShare(_bidAmount, flStakeShareRatio); + + validatorsBalanceMap[block.coinbase] += amtPayableToValidator; + validatorsTotal += amtPayableToValidator; + flStakeSharePayable += amtPayableToStakers; return _bidAmount; } @@ -459,8 +469,11 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { } } - validatorsBalanceMap[block.coinbase] += _bidAmount; - validatorsTotal += _bidAmount; + (uint256 amtPayableToValidator, uint256 amtPayableToStakers) = _calculateStakeShare(_bidAmount, flStakeShareRatio); + + validatorsBalanceMap[block.coinbase] += amtPayableToValidator; + validatorsTotal += amtPayableToValidator; + flStakeSharePayable += amtPayableToStakers; return _bidAmount; } @@ -486,10 +499,12 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { // Calculate the split of payment uint256 validatorShare = (getValidatorRefundShare(block.coinbase) * bidAmount) / VALIDATOR_REFUND_SCALE; uint256 refundAmount = bidAmount - validatorShare; // subtract to ensure no overflow + (uint256 amtPayableToValidator, uint256 amtPayableToStakers) = _calculateStakeShare(validatorShare, flStakeShareRatio); // Update balance and make payment - validatorsBalanceMap[block.coinbase] += validatorShare; - validatorsTotal += validatorShare; + validatorsBalanceMap[block.coinbase] += amtPayableToValidator; + validatorsTotal += amtPayableToValidator; + flStakeSharePayable += amtPayableToStakers; payable(refundAddress).transfer(refundAmount); emit RelayFlashBidWithRefund( @@ -504,6 +519,38 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { ); } + /// @notice Internal, calculates shares + /// @param _amount Amount to calculates cuts from + /// @param _share Share bps + /// @return validatorCut Validator cut + /// @return stakeCut Stake cut + function _calculateStakeShare(uint256 _amount, uint24 _share) internal pure returns (uint256 validatorCut, uint256 stakeCut) { + validatorCut = (_amount * (FEE_BASE - _share)) / FEE_BASE; + stakeCut = _amount - validatorCut; + } + + /// @notice Withdraws fl stake share + /// @dev fl only + /// @param _recipient Recipient + /// @param _amount Amount + function withdrawStakeShare(address _recipient, uint256 _amount) external onlyStakeShareRecipient nonReentrant { + if (_recipient == address(0) || _amount == 0) revert RelayCannotBeZero(); + flStakeSharePayable -= _amount; + SafeTransferLib.safeTransferETH( + _recipient, + _amount + ); + emit RelayProcessingWithdrewStakeShare(_recipient, _amount); + } + + function getCurrentStakeRatio() public view returns (uint24) { + return flStakeShareRatio; + } + + function getCurrentStakeBalance() public view returns (uint256) { + return flStakeSharePayable; + } + receive() external payable {} fallback() external payable {} @@ -761,6 +808,11 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { _; } + modifier onlyStakeShareRecipient() { + if (msg.sender != StakeShareRecipient) revert RelayOnlyStakeShareRecipient(); + _; + } + /// @notice Validates incoming bid /// @dev /// @param _oppTxHash Target Transaction hash diff --git a/contracts/interfaces/IFastLaneAuctionHandler.sol b/contracts/interfaces/IFastLaneAuctionHandler.sol index ff634f7..e624d21 100644 --- a/contracts/interfaces/IFastLaneAuctionHandler.sol +++ b/contracts/interfaces/IFastLaneAuctionHandler.sol @@ -45,6 +45,7 @@ interface IFastLaneAuctionHandler { uint256 newGasPrice ); event RelayProcessingPaidValidator(address indexed validator, uint256 validatorPayment, address indexed initiator); + event RelayProcessingWithdrewStakeShare(address indexed recipient, uint256 amountWithdrawn); event RelaySimulatedFlashBid( address indexed sender, uint256 amount, @@ -107,5 +108,6 @@ interface IFastLaneAuctionHandler { function validatorsBalanceMap(address) external view returns (uint256); function validatorsRefundShareMap(address) external view returns (uint256); function validatorsTotal() external view returns (uint256); + function withdrawStakeShare(address _recipient, uint256 _amount) external; function withdrawStuckERC20(address _tokenAddress) external; } From 007a56c34b78c9b11b428c5c98d60c053560b132 Mon Sep 17 00:00:00 2001 From: Jordan Hagan Date: Mon, 26 Feb 2024 11:33:42 +1300 Subject: [PATCH 2/5] Implemented new excess balance handling --- .../FastLaneAuctionHandler.sol | 86 ++++++------------- .../interfaces/IFastLaneAuctionHandler.sol | 3 +- test/PFL_AuctionHandler.t.sol | 29 ------- 3 files changed, 28 insertions(+), 90 deletions(-) diff --git a/contracts/auction-handler/FastLaneAuctionHandler.sol b/contracts/auction-handler/FastLaneAuctionHandler.sol index 5061bf3..60303ac 100644 --- a/contracts/auction-handler/FastLaneAuctionHandler.sol +++ b/contracts/auction-handler/FastLaneAuctionHandler.sol @@ -42,10 +42,9 @@ abstract contract FastLaneAuctionHandlerEvents { ); event RelayWithdrawStuckERC20(address indexed receiver, address indexed token, uint256 amount); - event RelayWithdrawStuckNativeToken(address indexed receiver, uint256 amount); + event RelayWithdrawExcessBalance(address indexed receiver, uint256 amount); event RelayProcessingPaidValidator(address indexed validator, uint256 validatorPayment, address indexed initiator); - event RelayProcessingWithdrewStakeShare(address indexed recipient, uint256 amountWithdrawn); event RelayFeeCollected(address indexed payor, address indexed payee, uint256 amount); @@ -100,7 +99,6 @@ abstract contract FastLaneAuctionHandlerEvents { error RelayCustomPayoutCantBePartial(); error RelayUnapprovedReentrancy(); - error RelayOnlyStakeShareRecipient(); } /// @notice Validator Data Struct @@ -135,7 +133,7 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { uint256 internal constant VALIDATOR_REFUND_SCALE = 10_000; // 1 = 0.01% /// @notice The default refund share for validators - int256 internal constant DEFAULT_VALIDATOR_REFUND_SHARE = 5000; // 50% + int256 internal constant DEFAULT_VALIDATOR_REFUND_SHARE = 5_000; // 50% /// @notice Mapping to Validator Data Struct mapping(address => ValidatorData) internal validatorsDataMap; @@ -157,10 +155,11 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { /// @notice Map[validator] = % payment to validator in a bid with refund mapping(address => int256) private validatorsRefundShareMap; - uint24 internal constant FEE_BASE = 1_000_000; - uint24 internal constant flStakeShareRatio = 50_000; // 5% - address internal constant StakeShareRecipient = address(0x01); - uint256 public flStakeSharePayable; + uint24 internal constant VALIDATOR_SHARE_BASE = 1_000_000; + uint24 internal constant STAKE_RATIO = 50_000; // 5% + + // TODO: update to point to the correct address + address internal excessBalanceRecipient = address(0); bytes32 private constant UNLOCKED = bytes32(uint256(1)); bytes32 private constant LOCKED = bytes32(uint256(2)); @@ -438,11 +437,10 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { _bidAmount = address(this).balance - balanceBefore; } - (uint256 amtPayableToValidator, uint256 amtPayableToStakers) = _calculateStakeShare(_bidAmount, flStakeShareRatio); + uint256 amtPayableToValidator = _calculateValidatorShare(_bidAmount, STAKE_RATIO); validatorsBalanceMap[block.coinbase] += amtPayableToValidator; validatorsTotal += amtPayableToValidator; - flStakeSharePayable += amtPayableToStakers; return _bidAmount; } @@ -469,11 +467,10 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { } } - (uint256 amtPayableToValidator, uint256 amtPayableToStakers) = _calculateStakeShare(_bidAmount, flStakeShareRatio); + uint256 amtPayableToValidator = _calculateValidatorShare(_bidAmount, STAKE_RATIO); validatorsBalanceMap[block.coinbase] += amtPayableToValidator; validatorsTotal += amtPayableToValidator; - flStakeSharePayable += amtPayableToStakers; return _bidAmount; } @@ -499,12 +496,11 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { // Calculate the split of payment uint256 validatorShare = (getValidatorRefundShare(block.coinbase) * bidAmount) / VALIDATOR_REFUND_SCALE; uint256 refundAmount = bidAmount - validatorShare; // subtract to ensure no overflow - (uint256 amtPayableToValidator, uint256 amtPayableToStakers) = _calculateStakeShare(validatorShare, flStakeShareRatio); + uint256 amtPayableToValidator = _calculateValidatorShare(validatorShare, STAKE_RATIO); // Update balance and make payment validatorsBalanceMap[block.coinbase] += amtPayableToValidator; validatorsTotal += amtPayableToValidator; - flStakeSharePayable += amtPayableToStakers; payable(refundAddress).transfer(refundAmount); emit RelayFlashBidWithRefund( @@ -523,32 +519,12 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { /// @param _amount Amount to calculates cuts from /// @param _share Share bps /// @return validatorCut Validator cut - /// @return stakeCut Stake cut - function _calculateStakeShare(uint256 _amount, uint24 _share) internal pure returns (uint256 validatorCut, uint256 stakeCut) { - validatorCut = (_amount * (FEE_BASE - _share)) / FEE_BASE; - stakeCut = _amount - validatorCut; - } - - /// @notice Withdraws fl stake share - /// @dev fl only - /// @param _recipient Recipient - /// @param _amount Amount - function withdrawStakeShare(address _recipient, uint256 _amount) external onlyStakeShareRecipient nonReentrant { - if (_recipient == address(0) || _amount == 0) revert RelayCannotBeZero(); - flStakeSharePayable -= _amount; - SafeTransferLib.safeTransferETH( - _recipient, - _amount - ); - emit RelayProcessingWithdrewStakeShare(_recipient, _amount); + function _calculateValidatorShare(uint256 _amount, uint24 _share) internal pure returns (uint256 validatorCut) { + validatorCut = (_amount * (VALIDATOR_SHARE_BASE - _share)) / VALIDATOR_SHARE_BASE; } function getCurrentStakeRatio() public view returns (uint24) { - return flStakeShareRatio; - } - - function getCurrentStakeBalance() public view returns (uint256) { - return flStakeSharePayable; + return STAKE_RATIO; } receive() external payable {} @@ -561,23 +537,6 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { * |__________________________________ */ - /// @notice Syncs stuck matic to calling validator - /// @dev In the event something went really wrong / vuln report - function syncStuckNativeToken() external onlyActiveValidators nonReentrant { - uint256 _expectedBalance = validatorsTotal; - uint256 _currentBalance = address(this).balance; - if (_currentBalance >= _expectedBalance) { - address _validator = getValidator(); - - uint256 _surplus = _currentBalance - _expectedBalance; - - validatorsBalanceMap[_validator] += _surplus; - validatorsTotal += _surplus; - - emit RelayWithdrawStuckNativeToken(_validator, _surplus); - } - } - /// @notice Withdraws stuck ERC20 /// @dev In the event people send ERC20 instead of Matic we can send them back /// @param _tokenAddress Address of the stuck token @@ -591,6 +550,14 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { } } + /// @notice Updates the excess balance recipient address + /// @dev Callable by the current recipient only + /// @param newRecipient New recipient address + function updateExcessBalanceRecipient(address newRecipient) external { + require(msg.sender == excessBalanceRecipient, "Caller is not the current excess balance recipient"); + excessBalanceRecipient = newRecipient; + } + /** * | * | Validator Functions | @@ -610,6 +577,12 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { validatorsBalanceMap[_validator] = 1; validatorsDataMap[_validator].blockOfLastWithdraw = uint64(block.number); SafeTransferLib.safeTransferETH(validatorPayee(_validator), payableBalance); + uint256 totalBalance = address(this).balance; + uint256 excessBalance = totalBalance - validatorsTotal; + if (excessBalance > 0) { + SafeTransferLib.safeTransferETH(excessBalanceRecipient, excessBalance); + emit RelayWithdrawExcessBalance(excessBalanceRecipient, excessBalance); + } emit RelayProcessingPaidValidator(_validator, payableBalance, msg.sender); return payableBalance; } @@ -808,11 +781,6 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { _; } - modifier onlyStakeShareRecipient() { - if (msg.sender != StakeShareRecipient) revert RelayOnlyStakeShareRecipient(); - _; - } - /// @notice Validates incoming bid /// @dev /// @param _oppTxHash Target Transaction hash diff --git a/contracts/interfaces/IFastLaneAuctionHandler.sol b/contracts/interfaces/IFastLaneAuctionHandler.sol index e624d21..866adeb 100644 --- a/contracts/interfaces/IFastLaneAuctionHandler.sol +++ b/contracts/interfaces/IFastLaneAuctionHandler.sol @@ -45,7 +45,6 @@ interface IFastLaneAuctionHandler { uint256 newGasPrice ); event RelayProcessingPaidValidator(address indexed validator, uint256 validatorPayment, address indexed initiator); - event RelayProcessingWithdrewStakeShare(address indexed recipient, uint256 amountWithdrawn); event RelaySimulatedFlashBid( address indexed sender, uint256 amount, @@ -55,7 +54,7 @@ interface IFastLaneAuctionHandler { ); event RelayValidatorPayeeUpdated(address validator, address payee, address indexed initiator); event RelayWithdrawStuckERC20(address indexed receiver, address indexed token, uint256 amount); - event RelayWithdrawStuckNativeToken(address indexed receiver, uint256 amount); + event RelayWithdrawExcessBalance(address indexed receiver, uint256 amount); function clearValidatorPayee() external; function collectFees() external returns (uint256); diff --git a/test/PFL_AuctionHandler.t.sol b/test/PFL_AuctionHandler.t.sol index fee3b0e..802a90c 100644 --- a/test/PFL_AuctionHandler.t.sol +++ b/test/PFL_AuctionHandler.t.sol @@ -665,35 +665,6 @@ contract PFLAuctionHandlerTest is PFLHelper, FastLaneAuctionHandlerEvents, Test assertEq(PFR.getValidatorPayee(VALIDATOR1), PAYEE1); } - function testSyncNativeTokenCanOnlyBeCalledByValidators() public { - _donateOneWeiToValidatorBalance(); - uint256 stuckNativeAmount = 1 ether; - vm.prank(USER); - address(PFR).call{value: stuckNativeAmount}(""); - - vm.prank(USER); - vm.expectRevert(FastLaneAuctionHandlerEvents.RelayNotActiveValidator.selector); - PFR.syncStuckNativeToken(); - - uint256 validatorBalanceBefore = PFR.getValidatorBalance(VALIDATOR1); - vm.prank(VALIDATOR1); - PFR.syncStuckNativeToken(); - uint256 validatorBalanceAfter = PFR.getValidatorBalance(VALIDATOR1); - assertEq(validatorBalanceAfter - validatorBalanceBefore, stuckNativeAmount); - } - - function testSyncNativeTokenDoesNotIncreaseBalanceIfNoExcess() public { - _donateOneWeiToValidatorBalance(); - uint256 auctionContractBalanceBefore = address(PFR).balance; - uint256 validatorBalanceBefore = PFR.getValidatorBalance(VALIDATOR1); - vm.prank(VALIDATOR1); - PFR.syncStuckNativeToken(); - uint256 auctionContractBalanceAfter = address(PFR).balance; - uint256 validatorBalanceAfter = PFR.getValidatorBalance(VALIDATOR1); - assertEq(validatorBalanceBefore, validatorBalanceAfter); - assertEq(auctionContractBalanceBefore, auctionContractBalanceAfter); - } - function testWithdrawStuckERC20CanOnlyBeCalledByValidators() public { _donateOneWeiToValidatorBalance(); uint256 stuckERC20Amount = 1 ether; From 78090009c7efbda3e23fe5a50606cc066ec87bde Mon Sep 17 00:00:00 2001 From: Jordan Hagan Date: Mon, 26 Feb 2024 14:30:41 +1300 Subject: [PATCH 3/5] Added test for excess balance shipping --- test/PFL_AuctionHandler.t.sol | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/PFL_AuctionHandler.t.sol b/test/PFL_AuctionHandler.t.sol index 802a90c..f38ebcc 100644 --- a/test/PFL_AuctionHandler.t.sol +++ b/test/PFL_AuctionHandler.t.sol @@ -354,9 +354,12 @@ contract PFLAuctionHandlerTest is PFLHelper, FastLaneAuctionHandlerEvents, Test function testCollectFees() public { vm.deal(SEARCHER_ADDRESS1, 100 ether); + address EXCESS_RECIPIENT = address(0); uint256 bidAmount = 2 ether; - uint256 expectedValidatorPayout = bidAmount - 1; + uint256 validatorCut = (bidAmount * (1_000_000 - 50_000)) / 1_000_000; + uint256 excessBalance = bidAmount - validatorCut; + uint256 expectedValidatorPayout = validatorCut - 1; bytes32 oppTx = bytes32("tx1"); bytes memory searcherUnusedData = abi.encodeWithSignature("unused()"); @@ -368,13 +371,20 @@ contract PFLAuctionHandlerTest is PFLHelper, FastLaneAuctionHandlerEvents, Test uint256 snap = vm.snapshot(); // As V1 pay itself + uint256 excessRecipientBalanceBefore = EXCESS_RECIPIENT.balance; uint256 balanceBefore = VALIDATOR1.balance; vm.expectEmit(true, true, true, true); emit RelayProcessingPaidValidator(VALIDATOR1, expectedValidatorPayout, VALIDATOR1); + emit RelayWithdrawExcessBalance(address(0), excessBalance); vm.prank(VALIDATOR1); + uint256 returnedAmountPaid = PFR.collectFees(); uint256 actualAmountPaid = VALIDATOR1.balance - balanceBefore; + uint256 excessRecipientBalanceAfter = EXCESS_RECIPIENT.balance; + + // Excess balance was sent to the excess recipient + assertEq(excessRecipientBalanceAfter - excessRecipientBalanceBefore, excessBalance); // Validator actually got paid as expected assertEq(returnedAmountPaid, expectedValidatorPayout); From 71e9bb5badc8506d9027de559e99692504b66473 Mon Sep 17 00:00:00 2001 From: Jordan Hagan Date: Mon, 26 Feb 2024 14:34:18 +1300 Subject: [PATCH 4/5] tidy naming --- contracts/auction-handler/FastLaneAuctionHandler.sol | 4 ++-- contracts/interfaces/IFastLaneAuctionHandler.sol | 2 +- test/PFL_AuctionHandler.t.sol | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/auction-handler/FastLaneAuctionHandler.sol b/contracts/auction-handler/FastLaneAuctionHandler.sol index 60303ac..9526fe3 100644 --- a/contracts/auction-handler/FastLaneAuctionHandler.sol +++ b/contracts/auction-handler/FastLaneAuctionHandler.sol @@ -42,7 +42,7 @@ abstract contract FastLaneAuctionHandlerEvents { ); event RelayWithdrawStuckERC20(address indexed receiver, address indexed token, uint256 amount); - event RelayWithdrawExcessBalance(address indexed receiver, uint256 amount); + event RelayProcessingExcessBalance(address indexed receiver, uint256 amount); event RelayProcessingPaidValidator(address indexed validator, uint256 validatorPayment, address indexed initiator); @@ -581,7 +581,7 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { uint256 excessBalance = totalBalance - validatorsTotal; if (excessBalance > 0) { SafeTransferLib.safeTransferETH(excessBalanceRecipient, excessBalance); - emit RelayWithdrawExcessBalance(excessBalanceRecipient, excessBalance); + emit RelayProcessingExcessBalance(excessBalanceRecipient, excessBalance); } emit RelayProcessingPaidValidator(_validator, payableBalance, msg.sender); return payableBalance; diff --git a/contracts/interfaces/IFastLaneAuctionHandler.sol b/contracts/interfaces/IFastLaneAuctionHandler.sol index 866adeb..fb11fa3 100644 --- a/contracts/interfaces/IFastLaneAuctionHandler.sol +++ b/contracts/interfaces/IFastLaneAuctionHandler.sol @@ -54,7 +54,7 @@ interface IFastLaneAuctionHandler { ); event RelayValidatorPayeeUpdated(address validator, address payee, address indexed initiator); event RelayWithdrawStuckERC20(address indexed receiver, address indexed token, uint256 amount); - event RelayWithdrawExcessBalance(address indexed receiver, uint256 amount); + event RelayProcessingExcessBalance(address indexed receiver, uint256 amount); function clearValidatorPayee() external; function collectFees() external returns (uint256); diff --git a/test/PFL_AuctionHandler.t.sol b/test/PFL_AuctionHandler.t.sol index f38ebcc..b82e9ef 100644 --- a/test/PFL_AuctionHandler.t.sol +++ b/test/PFL_AuctionHandler.t.sol @@ -375,7 +375,7 @@ contract PFLAuctionHandlerTest is PFLHelper, FastLaneAuctionHandlerEvents, Test uint256 balanceBefore = VALIDATOR1.balance; vm.expectEmit(true, true, true, true); emit RelayProcessingPaidValidator(VALIDATOR1, expectedValidatorPayout, VALIDATOR1); - emit RelayWithdrawExcessBalance(address(0), excessBalance); + emit RelayProcessingExcessBalance(address(0), excessBalance); vm.prank(VALIDATOR1); From fa0e194215bd38122394e6f646db4114717dbcab Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Wed, 28 Feb 2024 11:50:42 +0200 Subject: [PATCH 5/5] Change new constants to uint256 for gas optimization --- .../FastLaneAuctionHandler.sol | 61 +++++++++---------- .../interfaces/IFastLaneAuctionHandler.sol | 9 ++- test/PFL_AuctionHandler.t.sol | 2 +- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/contracts/auction-handler/FastLaneAuctionHandler.sol b/contracts/auction-handler/FastLaneAuctionHandler.sol index 9526fe3..b3268f9 100644 --- a/contracts/auction-handler/FastLaneAuctionHandler.sol +++ b/contracts/auction-handler/FastLaneAuctionHandler.sol @@ -154,9 +154,9 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { /// @notice Map[validator] = % payment to validator in a bid with refund mapping(address => int256) private validatorsRefundShareMap; - - uint24 internal constant VALIDATOR_SHARE_BASE = 1_000_000; - uint24 internal constant STAKE_RATIO = 50_000; // 5% + + uint256 internal constant VALIDATOR_SHARE_BASE = 1_000_000; + uint256 internal constant STAKE_RATIO = 50_000; // 5% // TODO: update to point to the correct address address internal excessBalanceRecipient = address(0); @@ -255,16 +255,15 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { /// @notice Submits a fast bid /// @dev Will not revert /// @param fastGasPrice Bonus gasPrice rate that Searcher commits to pay to validator for gas used by searcher's call - /// @param executeOnLoss Boolean flag that enables Searcher calls to execute even if they lost the auction. + /// @param executeOnLoss Boolean flag that enables Searcher calls to execute even if they lost the auction. /// @param searcherToAddress Searcher contract address to be called on its `fastLaneCall` function. /// @param searcherCallData callData to be passed to `_searcherToAddress.fastLaneCall(fastPrice,msg.sender,callData)` function submitFastBid( uint256 fastGasPrice, // surplus gasprice commited to be paid at the end of execution bool executeOnLoss, // If true, execute even if searcher lost auction address searcherToAddress, - bytes calldata searcherCallData + bytes calldata searcherCallData ) external payable onlyEOA nonReentrant { - if (searcherToAddress == address(this) || searcherToAddress == msg.sender) revert RelaySearcherWrongParams(); PGAData memory existing_bid = fulfilledPGAMap[block.number]; @@ -274,30 +273,31 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { bool alreadyPaid = existing_bid.paid; // NOTE: These checks help mitigate the damage to searchers caused by relay error and adversarial validators by reverting - // early if the transactions are not sequenced pursuant to auction rules. + // early if the transactions are not sequenced pursuant to auction rules. - // Do not execute if a fastBid tx with a lower gasPrice was executed prior to this tx in the same block. - // NOTE: This edge case should only be achieveable via validator manipulation or erratic searcher nonce management + // Do not execute if a fastBid tx with a lower gasPrice was executed prior to this tx in the same block. + // NOTE: This edge case should only be achieveable via validator manipulation or erratic searcher nonce management if (lowestGasPrice != 0 && lowestGasPrice < tx.gasprice) { - emit RelayInvestigateOutcome(block.coinbase, msg.sender, block.number, lowestFastPrice, fastGasPrice, lowestGasPrice, tx.gasprice); - - // Do not execute if a fastBid tx with a lower bid amount was executed prior to this tx in the same block. + emit RelayInvestigateOutcome( + block.coinbase, msg.sender, block.number, lowestFastPrice, fastGasPrice, lowestGasPrice, tx.gasprice + ); + + // Do not execute if a fastBid tx with a lower bid amount was executed prior to this tx in the same block. } else if (lowestTotalPrice != 0 && lowestTotalPrice <= fastGasPrice + tx.gasprice) { - emit RelayInvestigateOutcome(block.coinbase, msg.sender, block.number, lowestFastPrice, fastGasPrice, lowestGasPrice, tx.gasprice); - - // Execute the tx if there are no issues w/ ordering. - // Execute the tx if the searcher enabled executeOnLoss or if the searcher won - } else if (executeOnLoss || !alreadyPaid) { + emit RelayInvestigateOutcome( + block.coinbase, msg.sender, block.number, lowestFastPrice, fastGasPrice, lowestGasPrice, tx.gasprice + ); + // Execute the tx if there are no issues w/ ordering. + // Execute the tx if the searcher enabled executeOnLoss or if the searcher won + } else if (executeOnLoss || !alreadyPaid) { // Use a try/catch pattern so that tx.gasprice and bidAmount can be saved to verify that - // proper transaction ordering is being followed. - try this.fastBidWrapper{value: msg.value}( - msg.sender, fastGasPrice, searcherToAddress, searcherCallData - ) returns (uint256 bidAmount) { - + // proper transaction ordering is being followed. + try this.fastBidWrapper{value: msg.value}(msg.sender, fastGasPrice, searcherToAddress, searcherCallData) + returns (uint256 bidAmount) { // Mark this auction as being complete to provide quicker reverts for subsequent searchers fulfilledPGAMap[block.number] = PGAData({ - lowestGasPrice: uint64(tx.gasprice), + lowestGasPrice: uint64(tx.gasprice), lowestFastPrice: uint64(fastGasPrice), lowestTotalPrice: uint64(fastGasPrice + tx.gasprice), paid: true @@ -306,27 +306,22 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { emit RelayFastBid(msg.sender, block.coinbase, true, bidAmount, searcherToAddress); return; // return early so that we don't refund the searcher's msg.value - } catch { // Update the auction to provide quicker reverts for subsequent searchers fulfilledPGAMap[block.number] = PGAData({ - lowestGasPrice: uint64(tx.gasprice), + lowestGasPrice: uint64(tx.gasprice), lowestFastPrice: uint64(fastGasPrice), lowestTotalPrice: uint64(fastGasPrice + tx.gasprice), paid: alreadyPaid // carry forward any previous wins in the block }); emit RelayFastBid(msg.sender, block.coinbase, false, 0, searcherToAddress); - } } if (msg.value > 0) { - // Refund the searcher any msg.value for failed txs. - SafeTransferLib.safeTransferETH( - msg.sender, - msg.value - ); + // Refund the searcher any msg.value for failed txs. + SafeTransferLib.safeTransferETH(msg.sender, msg.value); } } @@ -519,11 +514,11 @@ contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents { /// @param _amount Amount to calculates cuts from /// @param _share Share bps /// @return validatorCut Validator cut - function _calculateValidatorShare(uint256 _amount, uint24 _share) internal pure returns (uint256 validatorCut) { + function _calculateValidatorShare(uint256 _amount, uint256 _share) internal pure returns (uint256 validatorCut) { validatorCut = (_amount * (VALIDATOR_SHARE_BASE - _share)) / VALIDATOR_SHARE_BASE; } - function getCurrentStakeRatio() public view returns (uint24) { + function getCurrentStakeRatio() public view returns (uint256) { return STAKE_RATIO; } diff --git a/contracts/interfaces/IFastLaneAuctionHandler.sol b/contracts/interfaces/IFastLaneAuctionHandler.sol index fb11fa3..f0096d2 100644 --- a/contracts/interfaces/IFastLaneAuctionHandler.sol +++ b/contracts/interfaces/IFastLaneAuctionHandler.sol @@ -85,9 +85,12 @@ interface IFastLaneAuctionHandler { address searcherToAddress, bytes memory searcherCallData ) external payable; - function submitFastBid(uint256 fastGasPrice, bool executeOnLoss, address searcherToAddress, bytes memory searcherCallData) - external - payable; + function submitFastBid( + uint256 fastGasPrice, + bool executeOnLoss, + address searcherToAddress, + bytes memory searcherCallData + ) external payable; function submitFlashBid( uint256 bidAmount, bytes32 oppTxHash, diff --git a/test/PFL_AuctionHandler.t.sol b/test/PFL_AuctionHandler.t.sol index b82e9ef..1396cc7 100644 --- a/test/PFL_AuctionHandler.t.sol +++ b/test/PFL_AuctionHandler.t.sol @@ -378,7 +378,7 @@ contract PFLAuctionHandlerTest is PFLHelper, FastLaneAuctionHandlerEvents, Test emit RelayProcessingExcessBalance(address(0), excessBalance); vm.prank(VALIDATOR1); - + uint256 returnedAmountPaid = PFR.collectFees(); uint256 actualAmountPaid = VALIDATOR1.balance - balanceBefore; uint256 excessRecipientBalanceAfter = EXCESS_RECIPIENT.balance;