The Standard

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

claimRewards is not compatible with Fee-on-transfer Tokens

Summary

If the collateral assets are fee-on-transfer tokens, claimRewards will revert because of the accounting difference between the calculated rewards and the amount available to be transferred to reward recipient.

Vulnerability Details

There are some ERC-20 tokens that are applying fees on every transfer they made, a portion of the token's value is automatically deducted as a transfer fee or tax. This means that the address receiving the tokens will receive a slightly lower amount than the sender sent.
If these tokens become part of accepted tokens as collateral, there could be a problem in the claiming of rewards at the end.

Let's discuss on how this issue could be possible, by observing first the below code from function distributeAssets, in this code the _portion pertains to the portion of collateral asset that should be rewarded to the pool holder. It updates mapping rewards by this _portion amount. Rewards mapping pertains to the rewards allocated to the holder and the type of collateral asset to be rewarded.

File: LiquidationPool.sol
252: rewards[abi.encodePacked(_position.holder, asset.token.symbol)] += _portion;// updating the rewards by adding the holder's portion on collateral asset

After updating the rewards mapping, this will be used by function claimRewards so that the pool holder can receive their rewards.
However, the problems lies on line 193 below, it uses _rewardAmount from line 186 in which the figure came from the rewards mapping.

At this stage of claiming rewards, there is already a difference on accounting because the reward portion transferred from Liquidity Pool Manager contract to Liquidity Pool is already deducted, meaning the Liquidity Pool receive a lower number of tokens (reward portion). This will cause line 193 to revert because of accounting difference.

File: LiquidationPool.sol
182: function claimRewards() external {
183: ITokenManager.Token[] memory _tokens = ITokenManager(tokenManager).getAcceptedTokens(); //assign of array of collateral accepted assets
184: for (uint256 i = 0; i < _tokens.length; i++) { // looping in array of assets
185: ITokenManager.Token memory _token = _tokens[i];
186: uint256 _rewardAmount = rewards[abi.encodePacked(msg.sender, _token.symbol)];
187: if (_rewardAmount > 0) {
188: delete rewards[abi.encodePacked(msg.sender, _token.symbol)]; // deletes the rewards per msg.sender to avoid over rewarding
189: if (_token.addr == address(0)) {
190: (bool _sent,) = payable(msg.sender).call{value: _rewardAmount}(""); // if 0 address, sent native asset to msg.sender
191: require(_sent);
192: } else {
193: IERC20(_token.addr).transfer(msg.sender, _rewardAmount); // or else send the erc20 tokens to msg.sender
194: }
195: }

Proof of Concept

Consider the following scenario to illustrate the case

Step 1 Borrower deposited Fee-on-transfer tokens as collateral asset to smart vault.

Step 2 Collateral asset has been subject for liquidation and immediately will be transferred from smart vault to Liquidity Pool Manager contract.

Step 3 After calculation of reward portion for liquidators or pool holders, collateral assets will be transferred from Liquidity Pool Manager to Liquidity Pool.
Let's say pool holders is subject for reward of 10 fee-on-transfer tokens, but the moment it reached the liquidity pool it is already deducted by fee,
let's say for example 1% fee deduction, so the actual fee-on-transfer tokens inside the liquidity pool is only 9.9 but the actual reward is 10.

Step 4 Pool holders decided to claim rewards but failed on receiving the tokens because of the accounting difference (9.9 tokens available in liquidity pool
versus 10 tokens actual reward ).

Impact

Pool holders won't be able to receive their rewards.

Tools Used

Manual review

Recommendations

Don't allow fee on-transfer tokens to be deposited as collateral asset. This could bring problem on accounting and tracking of tokens since every transfer , there is a deduction on the recipient part.

Updates

Lead Judging Commences

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

fee-on-transfer

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Out of scope
Assigned finding tags:

fee-on-transfer

Support

FAQs

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