The Standard

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

ERC20 Tokens with Over 18 Decimals Causing Locked Funds

[M-02] ERC20 Tokens with Over 18 Decimals Causing Locked Funds

Description

While the protocol is compatible with tokens containing 18 decimals or fewer, it encounters issues with tokens having more than 18 decimals, such as YAMv2 with 24 decimals. The susceptible part of the code resides within LiquidationPool::distributeAssets(), specifically on line 220. Here, the costInEuros variable is calculated with 18 decimal places by scaling the _portion variable with 10 ** (18 - asset.token.dec). This calculation is prone to underflow when dealing with tokens having more than 18 decimals.

function distributeAssets(ILiquidationPoolManager.Asset[] memory _assets, uint256 _collateralRate, uint256 _hundredPC) external payable {
// ** code **
uint256 _portion = asset.amount * _positionStake / stakeTotal;
@> uint256 costInEuros = _portion * 10 ** (18 - asset.token.dec) * uint256(assetPriceUsd) / uint256(priceEurUsd) * _hundredPC / _collateralRate; // @audit underflow if asset.token.dec is more than 18
// ** code **
}

Impact

The vulnerability causes rewards from liquidated smart vaults to become permanently locked within the contract. Stakers will be unable to claim these rewards due to the consistent failure of LiquidationPool::distributeAssets() whenever assets with over 18 decimals are involved.

Proof of Concept

A demonstration using Foundry showcasing the reversion of LiquidationPool::distributeAssets() when employing the YAM mock token can be accessed here.

Recommended Mitigation

Consider augmenting the code to upscale and downscale the _portion variable, ensuring it maintains a consistent 18 decimal format without encountering underflow issues.

function distributeAssets(ILiquidationPoolManager.Asset[] memory _assets, uint256 _collateralRate, uint256 _hundredPC) external payable {
// ** code **
uint256 _portion = asset.amount * _positionStake / stakeTotal;
+ int8 decimalsDifference = 18 - int8(asset.token.dec);
+ if (decimalsDifference > 0) {
+ _portion = _portion * (10 ** uint256(int256(decimalsDifference)));
+ } else if (decimalsDifference < 0) {
+ _portion = _portion / (10 ** uint256(int256(-decimalsDifference)));
+ }
- uint256 costInEuros = _portion * 10 ** (18 - asset.token.dec) * uint256(assetPriceUsd) / uint256(priceEurUsd) * _hundredPC / _collateralRate;
+ uint256 costInEuros = _portion * uint256(assetPriceUsd) / uint256(priceEurUsd) * _hundredPC / _collateralRate;
// ** code **
}

Tools Used

Manual Review

Updates

Lead Judging Commences

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

informational/invalid

dyoff Submitter
over 1 year ago
hrishibhat Lead Judge
over 1 year ago
hrishibhat Lead Judge over 1 year 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.