Skip to content

Commit

Permalink
Implemented new excess balance handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ephess committed Feb 25, 2024
1 parent e618760 commit 007a56c
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 90 deletions.
86 changes: 27 additions & 59 deletions contracts/auction-handler/FastLaneAuctionHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -100,7 +99,6 @@ abstract contract FastLaneAuctionHandlerEvents {
error RelayCustomPayoutCantBePartial();

error RelayUnapprovedReentrancy();
error RelayOnlyStakeShareRecipient();
}

/// @notice Validator Data Struct
Expand Down Expand Up @@ -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;
Expand All @@ -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));
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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(
Expand All @@ -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 {}
Expand All @@ -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
Expand All @@ -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 |
Expand All @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions contracts/interfaces/IFastLaneAuctionHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
Expand Down
29 changes: 0 additions & 29 deletions test/PFL_AuctionHandler.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 007a56c

Please sign in to comment.