HardhatDeFi
15,000 USDC
View results
Submission Details
Severity: low
Invalid

Data provider have additional incentives to game price of reference asset in their favour

Summary

Data provider can also be a liquidity provider which provides them with additional incentives to maliciously submit incorrect reference assets values.

Vulnerability Details

Since removing liquidity will incur fees (in WTokens) to be provided to data providers who report prices of reference assets in the pool that determine the final payouts, data providers now have additional incentives to game price of reference asset in their favor in order to earn more collateral.

When a user removes liquidity, a certain portion in fees in terms of pool collateral will be set aside for the data provider. Once claimed, the data provider will have access to the WToken which allows them to use it to add liquidity into existing contigent pools.

If the data provider address that receives the WToken and adds liquidity into a contigent pool where the address is also the reporter of the price of the reference assets, this allows them to manipulate the price of the reference assets to allow them to claim more collateral in return.

In LibDIVA.sol

function _removeLiquidityLib(
RemoveLiquidityParams memory _removeLiquidityParams,
LibDIVAStorage.Pool storage _pool
) internal returns (uint256 collateralAmountRemovedNet) {
// Get reference to relevant storage slot
LibDIVAStorage.GovernanceStorage storage gs = LibDIVAStorage
._governanceStorage();
// Confirm that functionality is not paused
if (block.timestamp < gs.pauseReturnCollateralUntil)
revert ReturnCollateralPaused();
// Check if pool exists
if (!_poolExists(_pool)) revert NonExistentPool();
// If status is Confirmed, users should use `redeemPositionToken` function
// to withdraw collateral
if (_pool.statusFinalReferenceValue == LibDIVAStorage.Status.Confirmed)
revert FinalValueAlreadyConfirmed();
// Create reference to short and long position tokens for the given pool
IPositionToken shortToken = IPositionToken(_pool.shortToken);
IPositionToken longToken = IPositionToken(_pool.longToken);
// Check that `shortTokenHolder` and `longTokenHolder` own the corresponding
// `_amount` of short and long position tokens. In particular, this check will
// revert when a user tries to remove an amount that exceeds the overall position token
// supply which is the maximum amount that a user can own.
if (
shortToken.balanceOf(_removeLiquidityParams.shortTokenHolder) <
_removeLiquidityParams.amount ||
longToken.balanceOf(_removeLiquidityParams.longTokenHolder) <
_removeLiquidityParams.amount
) revert InsufficientShortOrLongBalance();
// Get fee parameters applicable for given `_poolId`
LibDIVAStorage.Fees memory _fees = gs.fees[_pool.indexFees];
uint256 _protocolFee;
uint256 _settlementFee;
if (_fees.protocolFee > 0) {
// Calculate protocol fees to charge (note that collateral amount
// to return is equal to `_amount`)
_protocolFee = _calcFee(
_fees.protocolFee,
_removeLiquidityParams.amount,
IERC20Metadata(_pool.collateralToken).decimals()
);
// User has to increase `_amount` if fee is 0
if (_protocolFee == 0) revert ZeroProtocolFee();
} // else _protocolFee = 0 (default value for uint256)
if (_fees.settlementFee > 0) {
// Calculate settlement fees to charge
_settlementFee = _calcFee(
_fees.settlementFee,
_removeLiquidityParams.amount,
IERC20Metadata(_pool.collateralToken).decimals()
);
// User has to increase `_amount` if fee is 0
if (_settlementFee == 0) revert ZeroSettlementFee();
} // else _settlementFee = 0 (default value for uint256)
// Burn short and long position tokens
shortToken.burn(
_removeLiquidityParams.shortTokenHolder,
_removeLiquidityParams.amount
);
longToken.burn(
_removeLiquidityParams.longTokenHolder,
_removeLiquidityParams.amount
);
// Allocate protocol fee to DIVA treasury. Fee is held within this
// contract and can be claimed via `claimFee` `ion.
// `collateralBalance` is reduced inside `_allocateFeeClaim`.
_allocateFeeClaim(
_removeLiquidityParams.poolId,
_pool,
_getCurrentTreasury(gs),
_protocolFee
);
// Reserve settlement fee for data provider which is not known at this stage.
// Fee will be allocated to actual data provider following final value
// confirmation and afterwards can be claimed via the `claimFee` function.
_reserveFeeClaim(_removeLiquidityParams.poolId, _pool, _settlementFee); // <@ audit fees in terms of WTokens are reserve for the data provider
// Collateral amount to return net of fees
collateralAmountRemovedNet =
_removeLiquidityParams.amount -
_protocolFee -
_settlementFee;
// Log removal of liquidity
emit LiquidityRemoved(
_removeLiquidityParams.poolId,
_removeLiquidityParams.longTokenHolder,
_removeLiquidityParams.shortTokenHolder,
_removeLiquidityParams.amount
);
}

POC

Consider the following scenario

  1. Alice, who is a data provider, receives WUSDC due to a user who calls AaveDIVAWrapper.removeLiquity

  2. After some time, she accumulates fees that results in 100 WUSDC

  3. She utilizes this 100 WUSDC to add liquidity into a existing contigent pool

  4. She intentionally submits the price of the reference asset in her favor

  5. She is now able to claim more collateral tokens (WTokens) back in return (more than her principal WToken).

Impact

Additional incentives for data providers to submit wrong price for reference assets.

Mitigation

Consider rewriting the logic or calling AaveDIVAWrapper.redeemWToken for fee claiming to convert back to the underlying collateral before transferring fees back to the data provider when they attempt to claim fees.

Updates

Lead Judging Commences

bube Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.