The Standard

The Standard
DeFiHardhat
20,000 USDC
View results
Submission Details
Severity: low
Invalid

No rewards will be claimable if at least one token blacklist `Liquidation::claimRewards` caller, of if callers is a contract with wrong implementation of receiver function

Description

Rewards are sent to LiquidationPool when an asset distribution occurs. Liquidation::claimRewards allows callers to claim rewards if they have any, but it enforce to claim all of their rewards. If the transfer of at least one of this rewards fails, no rewards will be claimable.

If a bug exploit one asset reard distribution (as one of the issue reported during this audit that affects only ETH), the caller is a smart contract with a wrong implementation of receive function, a whitelisted asset has a blocklist or pause mechanism (like USDC, USDT or PAXG) then non of the rewards will be claimable

Impact

Blacklist of holder from a whitelisted token or if a whitelisted token is paused DOS claiming of rest of whitelisted tokens.

POC

  1. USDC and ETH are whitelisted by Token Manager

  2. Alice deposit (TST: 10000, EUR: 10000).

  3. 1 ETH at a valuation of 1100 EUR is distributed, given that Alice is the only staker she is now enable to claim 1 ETH

  4. USDC blacklist Alice address

  5. Bob call LiquidationPool:distributeAsset to distribute 1 USDC, this is also paid with Alice EUR position and now she is enforced to claim it when she calls Liquidation::claimRewards

When Alice calls Liquidation::claimRewards function reverts given that she is blacklisted from USDC, she cannot even claim her 1 ETH

Recommended mitigation

Option A: Add a function that enable claiming a single reward

function claimSingleReward(uint256 tokenManagerIndex) external {
ITokenManager.Token[] memory _tokens = ITokenManager(tokenManager).getAcceptedTokens();
ITokenManager.Token memory _token = _tokens[i];
uint256 _rewardAmount = rewards[abi.encodePacked(msg.sender, _token.symbol)];
if (_rewardAmount > 0) {
delete rewards[abi.encodePacked(msg.sender, _token.symbol)];
if (_token.addr == address(0)) {
(bool _sent,) = payable(msg.sender).call{value: _rewardAmount}("");
require(_sent);
} else {
IERC20(_token.addr).transfer(msg.sender, _rewardAmount);
}
}
}

Option B: Add try catch block when executing token/ETH transfers in LiquidationPool::claimRewards

function claimRewardHandler(
address claimer,
bytes32 tokenSymbol,
) external{
uint256 _rewardAmount = rewards[abi.encodePacked(msg.sender, _token.symbol)];
if (_rewardAmount > 0) {
delete rewards[abi.encodePacked(claimer, _token.symbol)];
if (_token.addr == address(0)) {
(bool _sent,) = payable(claimer).call{value: _rewardAmount}("");
require(_sent);
} else {
IERC20(_token.addr).transfer(claimer _rewardAmount);
}
}
}
function claimRewards() external {
ITokenManager.Token[] memory _tokens = ITokenManager(tokenManager).getAcceptedTokens();
for (uint256 i = 0; i < _tokens.length; i++) {
ITokenManager.Token memory _token = _tokens[i];
try this.claimRewardHandler(msg.sender,_token.symbol){} catch{}
}
}
Updates

Lead Judging Commences

hrishibhat Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

informational/invalid

Support

FAQs

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

Give us feedback!