The Standard

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

Fee-on-transfer tokens aren't supported

Summary

Fee-on-transfer tokens aren't supported by the current implementation of claim reward.

Vulnerability Details

In the distributeAssets() function, reward is constant and saved on rewards mapping:

                    uint256 _portion = asset.amount * _positionStake / stakeTotal;   // <---
                    uint256 costInEuros = _portion * 10 ** (18 - asset.token.dec) * uint256(assetPriceUsd) / uint256(priceEurUsd)
                        * _hundredPC / _collateralRate;
                    if (costInEuros > _position.EUROs) {
                        _portion = _portion * _position.EUROs / costInEuros;  // <----
                        costInEuros = _position.EUROs;
                    }
                    _position.EUROs -= costInEuros;
                    rewards[abi.encodePacked(_position.holder, asset.token.symbol)] += _portion; // <--- save total rewards of the token
                    burnEuros += costInEuros;
                    if (asset.token.addr == address(0)) {
                        nativePurchased += _portion;
                    } else {
                        IERC20(asset.token.addr).safeTransferFrom(manager, address(this), _portion);  // <---- transfer token to `LiquidationPool` contract
                    }

Rewards is claimed by calling claimRewards() function:

function claimRewards() external {
    ITokenManager.Token[] memory _tokens = ITokenManager(tokenManager).getAcceptedTokens();
    for (uint256 i = 0; i < _tokens.length; i++) {
        ITokenManager.Token memory _token = _tokens[i];
        uint256 _rewardAmount = rewards[abi.encodePacked(msg.sender, _token.symbol)];    // <-- get rewards amount
        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);  // <-- transfer reward to user
            }   
        }
    }
}

If token is fee-on-transfer, the actual amount transferred to the LiquidationPool contract will be less than total _portion amount transfered to contract

Impact

Last holders will not able to claim rewards due to lack of token avaiable in the contract

Tools Used

Manual review

Recommendations

Transfer token directly to holders instead of saving in the contract.

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.