Kinetic Disclosed Report

Audit report Kinetic Audit Contest

Price Validation and Arithmetic Vulnerabilities in RedeemBurnRateCalculatorV3

Company
Created date
Feb 09 2025

Target

https://github.com/kinetic-market/public-money-market-contracts

Vulnerability Details

The shouldSkipBurnRate function in RedeemBurnRateCalculatorV3.sol contains several vulnerabilities related to price validation and arithmetic operations that could lead to incorrect calculations or system manipulation.

The issues could result in:

  • Incorrect burn rate calculations due to zero prices
  • Precision loss in token value calculations
  • Potential overflow in large balance/price scenarios
  • System manipulation through price oracle exploitation

Validation steps

Vulnerable function:

// file contracts/Tokenomics/RedeemBurnRateCalculatorV3.sol
 function shouldSkipBurnRate(
        address user,
        uint256 /*amount*/
    ) external view override returns (bool) {
        if (_exclusionListedAddresses.contains(user))
            return false;

        if (useOraclePrice) {
            uint256 price = priceOracle.getPrice(address(protocolToken));

            return ((price * esProtocolToken.balanceOf(user) / (10**esProtocolTokenDecimals)) > burnRateThresholdUSD);
        }
        uint amountOut = getAmountOut();

        uint esProtocolWNativePrice = amountOut * esProtocolToken.balanceOf(user) / (10**esProtocolTokenDecimals);

        uint sNativePrice =  priceOracle.getPrice(address(sETH));

        esProtocolWNativePrice = esProtocolWNativePrice * sNativePrice / (10**sETHDecimals);

        return esProtocolWNativePrice > burnRateThresholdUSD;
    }
  1. If oracle returns price = 0, calculations will return 0, leading to incorrect burn rate decisions
  2. Large token balances combined with prices could cause overflow
  3. Multiple divisions after multiplications can lead to significant precision loss

Recommended fix:

function shouldSkipBurnRate(address user, uint256 /*amount*/) external view returns (bool) {
    require(user != address(0), "Zero address");
    
    if (_exclusionListedAddresses.contains(user))
        return false;

    uint256 userBalance = esProtocolToken.balanceOf(user);
    if (userBalance == 0) return false;

    if (useOraclePrice) {
        uint256 price = priceOracle.getPrice(address(protocolToken));
        require(price > 0, "Invalid price");
        
        // Use SafeMath or checked arithmetic
        uint256 userValue = (price * userBalance) / (10**esProtocolTokenDecimals);
        return userValue > burnRateThresholdUSD;
    }

    uint256 amountOut = getAmountOut();
    require(amountOut > 0, "Invalid amount out");
    
    uint256 sNativePrice = priceOracle.getPrice(address(sETH));
    require(sNativePrice > 0, "Invalid sETH price");

    uint256 userValue = ((amountOut * userBalance) / (10**esProtocolTokenDecimals)) * 
                       sNativePrice / (10**sETHDecimals);
    
    return userValue > burnRateThresholdUSD;
}
CommentsReport History
Comments on this report are hidden
Details
Statedisclosed
Severity
None
Bounty$71
Visibilitypartially
VulnerabilityOther
Participants (3)
author
company admin