When users attempt to withdraw liquidity from the omnipool by calling the remove_liquidity function, a dynamic withdrawal fee is calculated based on current asset prices. This fee is calculated using the calculate_withdrawal_fee function from the omnipool math module, which takes into account the current pool price (spot price) and an external oracle price:
The calculate_withdrawal_fee function determines the withdrawal fee, which can vary from a minimum fee (min_fee) to a maximum fee (100% represented by FixedU128::one()):
This means that in highly volatile markets, users may be required to pay a higher fee than expected when calling remove_liquidity (as the asset price might change from when the tx is submitted and when it's executed), potentially resulting in loss of funds and receiving less asset from their position than anticipated. Since the remove_liquidity function does not implement any protection against price movements (e.g., a minimum amount to be received by the user), users have no safeguard against increases in the withdrawal fee.
Impact
Users may receive fewer asset funds than expected when withdrawing liquidity due to being compelled to pay a higher dynamic withdrawal fee.
Tools Used
Manual review, VS Code
Recommended Mitigation
To address this issue, it is recommended to modify the remove_liquidity function to include a max_fee_amount parameter provided by the users. This parameter will serve as a slippage protection, preventing users from paying a higher fee than anticipated.
By introducing the max_fee_amount parameter, users can limit their exposure to unexpected increases in withdrawal fees, providing them with greater control over their transactions.
Lines of code
https://github.com/code-423n4/2024-02-hydradx/blob/main/HydraDX-node/pallets/omnipool/src/lib.rs#L743-L764
Vulnerability details
Issue Description
When users attempt to withdraw liquidity from the omnipool by calling the
remove_liquidity
function, a dynamic withdrawal fee is calculated based on current asset prices. This fee is calculated using thecalculate_withdrawal_fee
function from the omnipool math module, which takes into account the current pool price (spot price) and an external oracle price:The
calculate_withdrawal_fee
function determines the withdrawal fee, which can vary from a minimum fee (min_fee
) to a maximum fee (100% represented byFixedU128::one()
):This means that in highly volatile markets, users may be required to pay a higher fee than expected when calling
remove_liquidity
(as the asset price might change from when the tx is submitted and when it's executed), potentially resulting in loss of funds and receiving less asset from their position than anticipated. Since theremove_liquidity
function does not implement any protection against price movements (e.g., a minimum amount to be received by the user), users have no safeguard against increases in the withdrawal fee.Impact
Users may receive fewer asset funds than expected when withdrawing liquidity due to being compelled to pay a higher dynamic withdrawal fee.
Tools Used
Manual review, VS Code
Recommended Mitigation
To address this issue, it is recommended to modify the
remove_liquidity
function to include amax_fee_amount
parameter provided by the users. This parameter will serve as a slippage protection, preventing users from paying a higher fee than anticipated.By introducing the
max_fee_amount
parameter, users can limit their exposure to unexpected increases in withdrawal fees, providing them with greater control over their transactions.Assessed type
Context