Centrifuge - HChang26's results

The institutional ecosystem for on-chain credit.

General Information

Platform: Code4rena

Start Date: 08/09/2023

Pot Size: $70,000 USDC

Total HM: 8

Participants: 84

Period: 6 days

Judge: gzeon

Total Solo HM: 2

Id: 285

League: ETH

Centrifuge

Findings Distribution

Researcher Performance

Rank: 12/84

Findings: 1

Award: $1,143.09

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: 0xStalin

Also found by: Aymen0909, HChang26, J4X, imtybik

Labels

bug
2 (Med Risk)
low quality report
satisfactory
duplicate-118

Awards

1143.0858 USDC - $1,143.09

External Links

Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/InvestmentManager.sol#L686

Vulnerability details

Impact

In _fromPriceDecimals() function. When both _value and decimals are relatively small numbers, this function may return zero. As a result, _decreaseDepositLimits() cannot properly update lpValues.

Proof of Concept

The _fromPriceDecimals() function is responsible for converting the decimal representation of a value from the price decimals back to its intended decimals. However, a specific scenario can lead to unexpected behavior.

When both _value and the decimals of the currency are relatively low numbers, especially when the decimals value results in a larger denominator, _fromPriceDecimals() may return a result of 0. Ex. _value = 10000000000 decimals = 6

    function _fromPriceDecimals(uint256 _value, uint8 decimals, address liquidityPool)
        internal
        view
        returns (uint128 value)
    {
        if (PRICE_DECIMALS == decimals) return _toUint128(_value);
        value = _toUint128(_value / 10 ** (PRICE_DECIMALS - decimals));
    }

Consider the following scenario: a user initiates the mint() function on the LiquidityPool contract with a particularly small value for shares. This action subsequently triggers the execution of the processMint() function within the InvestmentManager contract. During this process, the _currencyAmount variable is calculated using the _calculateCurrencyAmount() function.

       uint128 _currencyAmount = _calculateCurrencyAmount(_trancheTokenAmount, liquidityPool, depositPrice);
        _deposit(_trancheTokenAmount, _currencyAmount, liquidityPool, user);
        currencyAmount = uint256(_currencyAmount);

_calculateCurrencyAmount() calculates currencyAmountInPriceDecimals, it is then used to calculated currencyAmount in _fromPriceDecimals()

        uint256 currencyAmountInPriceDecimals = _toPriceDecimals(
            trancheTokenAmount, trancheTokenDecimals, liquidityPool
        ).mulDiv(price, 10 ** PRICE_DECIMALS, MathLib.Rounding.Down);

        currencyAmount = _fromPriceDecimals(currencyAmountInPriceDecimals, currencyDecimals, liquidityPool);

Assume currencyAmount = 0. _deposit() will only decrease trancheTokenAmount but not currencyAmount, yet user still receives trancheTokenAmount.

        _decreaseDepositLimits(user, liquidityPool, currencyAmount, trancheTokenAmount); // decrease the possible deposit limits
        require(lPool.checkTransferRestriction(msg.sender, user, 0), "InvestmentManager/trancheTokens-not-a-member");
        require(
            lPool.transferFrom(address(escrow), user, trancheTokenAmount),
            "InvestmentManager/trancheTokens-transfer-failed"
        );
    function _decreaseDepositLimits(address user, address liquidityPool, uint128 _currency, uint128 trancheTokens)
        internal
    {
        LPValues storage lpValues = orderbook[user][liquidityPool];
        if (lpValues.maxDeposit < _currency) {
            lpValues.maxDeposit = 0;
        } else {
            lpValues.maxDeposit = lpValues.maxDeposit - _currency;
        }
        if (lpValues.maxMint < trancheTokens) {
            lpValues.maxMint = 0;
        } else {
            lpValues.maxMint = lpValues.maxMint - trancheTokens;
        }
    }

Tools Used

Manual Review

    function _fromPriceDecimals(uint256 _value, uint8 decimals, address liquidityPool)
        internal
        view
        returns (uint128 value)
    {
        if (PRICE_DECIMALS == decimals) return _toUint128(_value);
        value = _toUint128(_value / 10 ** (PRICE_DECIMALS - decimals));
+       require(value != 0);
    }

Assessed type

Math

#0 - raymondfam

2023-09-15T06:38:41Z

Unlikely. _value has earlier been normalized to 1e18.

#1 - c4-pre-sort

2023-09-15T06:38:57Z

raymondfam marked the issue as low quality report

#2 - c4-pre-sort

2023-09-15T06:39:01Z

raymondfam marked the issue as primary issue

#3 - c4-judge

2023-09-25T16:48:38Z

gzeon-c4 marked the issue as duplicate of #118

#4 - c4-judge

2023-09-26T18:15:40Z

gzeon-c4 marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter