Incorrect calculation effectively removes fee
Description
When a queued withdrawal is deposited, the fee per strategy is calculated by getFeeAmount
:
function getFeeAmount() public view returns (uint256) {
uint256 gasSpendings = EigenLayerWithdrawalClaimGasCost;
uint256 feeAmount = gasSpendings * block.basefee;
return gasSpendings;
}
The constant EigenLayerWithdrawalClaimGasCost
is defined to be this:
uint32 internal constant EigenLayerWithdrawalClaimGasCost = 150_000;
As shown above, getFeeAmount
returns gasSpendings
instead of feeAmount
, which means it will always return 150,000 WEI. This amount of ETH is so small that the fee is effectively not present.
Additionally, when the fee should be withdrawn, the admin calls withdrawEther
:
function withdrawEther() external onlyAdmin {
// ... snip ...
if (accumulatedFee > 0.2 ether) {
uint256 amountToTreasury = accumulatedFee;
accumulatedFee = 0;
(sent, ) = payable(treasury).call{value: amountToTreasury, gas: 5000}("");
The quantity 0.2 Ether divided by 150,000 is about 10^12, which means it will take that many deposited strategies for the minimum threshold to be met.
Impact
Due to a coding mistake, the fee is reduced to a negligible quantity.
Recommendations
Although the obvious way to remediate this is to have getFeeAmount
return feeAmount
, we recommend against this strategy. This is because block.basefee
is controllable by the ETH validator mining the transaction that contains the block. Using MEV relayers like MEV-Share, a user can submit a transaction, pay the validator the transaction fee directly, and have the validator set block.baseFee
to zero. This results in the user not having to pay any fee to the treasury.
Instead, we recommend having this fee be a constant amount set by governance, or a constant amount per strategy depending on the typical gas usage of withdrawing from that particular strategy. This also protects against users paying high fees a second time if they submit a deposit during network congestion that incurs high fees, since the high fee would then be more than the gas cost the admin pays when completing the withdrawal.
Also, we recommend removing the 0.2 ETH minimum from withdrawEther
, since the admin can already be responsible to call it at a sensible rate, and if it is called too much, the only impact is that the admin pays an unnecessary amount of gas.
Remediation
This issue has been acknowledged by Gadze Finance SEZC, and a fix was implemented in commit 5217c0bc↗. This finding was remediated by having getFeeAmount
return feeAmount
. The part of this finding where this fee may be dodged using an MEV relayer was acknowledged as unlikely and low-risk.