Canto Dex Oracle contest - rbserver's results

Execution layer for original work.

General Information

Platform: Code4rena

Start Date: 07/09/2022

Pot Size: $20,000 CANTO

Total HM: 7

Participants: 65

Period: 1 day

Judge: 0xean

Total Solo HM: 3

Id: 159

League: ETH

Canto

Findings Distribution

Researcher Performance

Rank: 20/65

Findings: 2

Award: $146.62

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: hickuphh3

Also found by: 0xNazgul, 0xSky, CertoraInc, Deivitto, Jeiwan, SinceJuly, hansfriese, linmiaomiao, rbserver

Labels

bug
duplicate
2 (Med Risk)

Awards

664.9949 CANTO - $107.40

External Links

Lines of code

https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-periphery.sol#L549-L593 https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-periphery.sol#L487-L522 https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-core.sol#L201-L222 https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-core.sol#L424-L437 https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-core.sol#L237-L258

Vulnerability details

Impact

When calling the following getUnderlyingPrice function, the getPriceLP function below can be further executed.

https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-periphery.sol#L487-L522

    function getUnderlyingPrice(CToken ctoken) external override view returns(uint) {
         address underlying;
        { //manual scope to pop symbol off of stack
        string memory symbol = ctoken.symbol();
        if (compareStrings(symbol, "cCANTO")) {
            underlying = address(wcanto);
            return getPriceNote(address(wcanto), false);
        } else {
            underlying = address(ICErc20(address(ctoken)).underlying()); // We are getting the price for a CErc20 lending market
        }
		
        ...
        
        if (isPair(underlying)) { // this is an LP Token
            return getPriceLP(IBaseV1Pair(underlying));
        }
		
        ... 
    }

When calling the following getPriceLP function, the sample function below, which further calls the _getAmountOut function below, is executed to get the corresponding prices, and the sampleReserves function below is executed to get the corresponding assetReserves and unitReserves. Afterwards, token0TVL for each value of assetReserves and prices is calculated by executing uint token0TVL = assetReserves[i] * (prices[i] / decimals). Each token0TVL is then used to accumulate LpPricesCumulative by executing LpPricesCumulative += (token0TVL + token1TVL) * 1e18 / supply[i] . The LpPricesCumulative is further used to calculate LpPrice, which influences the return value of the getPriceLP function in a positive relationship, by executing uint LpPrice = LpPricesCumulative / 8.

https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-periphery.sol#L549-L593

    function getPriceLP(IBaseV1Pair pair) internal view returns(uint) {
        uint[] memory supply = pair.sampleSupply(8, 1);
        uint[] memory prices; 
        uint[] memory unitReserves; 
        uint[] memory assetReserves; 
        address token0 = pair.token0();
        address token1 = pair.token1();
        uint decimals;

        if (pair.stable()) { // stable pairs will be priced in terms of Note
            if (token0 == note) { //token0 is the unit, token1 will be priced with respect to this asset initially
                decimals = 10 ** (erc20(token1).decimals()); // we must normalize the price of token1 to 18 decimals
                prices = pair.sample(token1, decimals, 8, 1);
                (unitReserves, assetReserves) = pair.sampleReserves(8, 1);
            } else {
                decimals = 10 ** (erc20(token0).decimals());
                prices = pair.sample(token0, decimals, 8, 1);
                (assetReserves, unitReserves) = pair.sampleReserves(8, 1);
            }
        } else { // non-stable pairs will be priced in terms of Canto
            if (token0 == address(wcanto)) { // token0 is Canto, and the unit asset of this pair is Canto
                decimals = 10 ** (erc20(token1).decimals());
                prices = pair.sample(token1, decimals, 8, 1);
                (unitReserves, assetReserves) = pair.sampleReserves(8, 1);
            } else {
                decimals = 10 ** (erc20(token0)).decimals();
                prices = pair.sample(token0, decimals, 8, 1);
                (assetReserves, unitReserves) = pair.sampleReserves(8, 1);
            }
        }
        uint LpPricesCumulative;

        for(uint i; i < 8; ++i) {
            uint token0TVL = assetReserves[i] * (prices[i] / decimals);
            uint token1TVL = unitReserves[i]; // price of the unit asset is always 1
            LpPricesCumulative += (token0TVL + token1TVL) * 1e18 / supply[i];
        }
        uint LpPrice = LpPricesCumulative / 8; // take the average of the cumulative prices 
        
        if (pair.stable()) { // this asset has been priced in terms of Note
            return LpPrice;
        }
        // this asset has been priced in terms of Canto
        return LpPrice * getPriceNote(address(wcanto), false) / 1e18; // return the price in terms of Note
    }   

https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-core.sol#L201-L222

    function sample(address tokenIn, uint amountIn, uint points, uint window) public view returns (uint[] memory) {
        uint[] memory _prices = new uint[](points);

        uint lastIndex = observations.length-1;
        
        require(lastIndex >= points * window, "PAIR::NOT READY FOR PRICING"); //log if the price is requested and there are not enough observations
        
        uint i = lastIndex - (points * window); // point from which to begin the sample
        uint nextIndex = 0;
        uint index = 0;

        for (; i < lastIndex; i+=window) {
            nextIndex = i + window;
            uint timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp;
            uint _reserve0 = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed;
            uint _reserve1 = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed;
            _prices[index] = _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1);
            index = index + 1;
        }

        return _prices;
    }

https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-core.sol#L424-L437

    function _getAmountOut(uint amountIn, address tokenIn, uint _reserve0, uint _reserve1) internal view returns (uint) {
        if (stable) {
            uint xy =  _k(_reserve0, _reserve1);
            _reserve0 = _reserve0 * 1e18 / decimals0;
            _reserve1 = _reserve1 * 1e18 / decimals1;
            (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0);
            amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1;
            uint y = reserveB - _get_y(amountIn+reserveA, xy, reserveB);
            return y * (tokenIn == token0 ? decimals1 : decimals0) / 1e18;
        } else {
            (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0);
            return amountIn * reserveB / (reserveA + amountIn);
        }
    }

https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-core.sol#L237-L258

    function sampleReserves(uint points, uint window) public view returns (uint[] memory, uint[] memory) {
        uint[] memory _reserves0 = new uint[](points);
        uint[] memory _reserves1 = new uint[](points);
        
        uint lastIndex = observations.length-1;
        require(lastIndex >= points * window, "PAIR::NOT READY FOR PRICING");
        uint i = lastIndex - (points * window); // point from which to begin the sample
        uint nextIndex = 0;
        uint index = 0;
        uint timeElapsed;

        for(; i < lastIndex; i+=window) {
            nextIndex = i + window;
            timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp;
            _reserves0[index] = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed;
            _reserves1[index] = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed;
            
            index = index + 1;
        }

        return (_reserves0, _reserves1);
    }

In a situation where unitReserves[i] is less than, equal to, or greater than but close to assetReserves[i] when calling the getPriceLP function, prices[i] can be calculated to be less than decimals after calling sample and _getAmountOut, which causes the corresponding token0TVL to be 0 because uint token0TVL = assetReserves[i] * (prices[i] / decimals) based on the current implementation. However, token0TVL is expected to be more than 0 in this case. With token0TVL being 0, LpPricesCumulative and LpPrice are less than expected, which causes the return values of the getPriceLP and getUnderlyingPrice functions to be less than expected as well.

Because the "Comptroller calls this [getUnderlyingPrice] method when calculating accountLiquidities for users who hold balances of ERC20 tokens", as stated by the documentation, an incorrect return value of getUnderlyingPrice would lead to an incorrect accountLiquidities. Incorrect accountLiquidities can cause users' accounts to be subject to liquidation incorrectly, and users could lose their account assets as a result.

Proof of Concept

First, please add the following helper contract that simulates the current implementation and provides a suggested implementation for calculating token0TVL in the getPriceLP function.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;

contract Token0TVLCalculations {
    // decimals_ simulates 10 ** (erc20(token1).decimals()) or 10 ** (erc20(token0).decimals()) in BaseV1Router01.getPriceLP
    uint256 decimals_ = 10 ** 18;

    // amountIn_ simulates amountIn input for BaseV1Pair.sample and BaseV1Pair._getAmountOut
    // note that calling BaseV1Router01.getPriceLP uses decimals as amountIn input for BaseV1Pair.sample
    uint256 amountIn_ = decimals_;

    // calculateToken0TVLLt simulates a situation where unitReserves[i] is less than assetReserves[i] when calling BaseV1Router01.getPriceLP for calculating token0TVL
    function calculateToken0TVLLt() external view returns (uint256, uint256) {
        // reserve0_ simulates _reserve0 in BaseV1Pair.sample, reserveB in BaseV1Pair._getAmountOut, _reserves0[index] in BaseV1Pair.sampleReserves, and unitReserves[i] in BaseV1Router01.getPriceLP
        uint256 reserve0_ = 900_000 * 10 ** 18;

        // reserve1_ simulates _reserve1 in BaseV1Pair.sample, reserveA in BaseV1Pair._getAmountOut, _reserves1[index] in BaseV1Pair.sampleReserves, and assetReserves[i] in BaseV1Router01.getPriceLP
        uint256 reserve1_ = 1_000_000 * 10 ** 18;

        return calculateToken0TVL(reserve0_, reserve1_);
    }

    // calculateToken0TVLEq simulates a situation where unitReserves[i] is equal to assetReserves[i] when calling BaseV1Router01.getPriceLP for calculating token0TVL
    function calculateToken0TVLEq() external view returns (uint256, uint256) {
        // reserve0_ simulates _reserve0 in BaseV1Pair.sample, reserveB in BaseV1Pair._getAmountOut, _reserves0[index] in BaseV1Pair.sampleReserves, and unitReserves[i] in BaseV1Router01.getPriceLP
        uint256 reserve0_ = 1_000_000 * 10 ** 18;

        // reserve1_ simulates _reserve1 in BaseV1Pair.sample, reserveA in BaseV1Pair._getAmountOut, _reserves1[index] in BaseV1Pair.sampleReserves, and assetReserves[i] in BaseV1Router01.getPriceLP
        uint256 reserve1_ = 1_000_000 * 10 ** 18;

        return calculateToken0TVL(reserve0_, reserve1_);
    }

    // calculateToken0TVLGc simulates a situation where unitReserves[i] is greater than but close to assetReserves[i] when calling BaseV1Router01.getPriceLP for calculating token0TVL
    function calculateToken0TVLGc() external view returns (uint256, uint256) {
        // reserve0_ simulates _reserve0 in BaseV1Pair.sample, reserveB in BaseV1Pair._getAmountOut, _reserves0[index] in BaseV1Pair.sampleReserves, and unitReserves[i] in BaseV1Router01.getPriceLP
        uint256 reserve0_ = 1_000_000 * 10 ** 18 + 1_000_000;

        // reserve1_ simulates _reserve1 in BaseV1Pair.sample, reserveA in BaseV1Pair._getAmountOut, _reserves1[index] in BaseV1Pair.sampleReserves, and assetReserves[i] in BaseV1Router01.getPriceLP
        uint256 reserve1_ = 1_000_000 * 10 ** 18;

        return calculateToken0TVL(reserve0_, reserve1_);
    }

    function calculateToken0TVL(uint256 reserve0_, uint256 reserve1_) private view returns (uint256 token0TVLCurrent, uint256 token0TVLSuggested) {
        // price_ simulates prices[i] in BaseV1Router01.getPriceLP
        // for example, the calculation for price_ simulates amountIn * reserveB / (reserveA + amountIn), which is the calculation associated with volatile pair in BaseV1Pair._getAmountOut
        uint256 price_ = amountIn_ * reserve0_ / (reserve1_ + amountIn_);

        // the calculation for token0TVLCurrent simulates assetReserves[i] * (prices[i] / decimals), which is the current implementation's calculation for token0TVL in BaseV1Router01.getPriceLP
        token0TVLCurrent = reserve1_ * (price_ / decimals_);

        // the calculation for token0TVLSuggested simulates assetReserves[i] * prices[i] / decimals, which is the suggested calculation for token0TVL that can be implemented in BaseV1Router01.getPriceLP
        token0TVLSuggested = reserve1_ * price_ / decimals_;
    }
}

Then, please append the following test in the Testing LpToken Price Accuracy after large vol moves in Canto/Note pair describe block in test\canto\Oracle\oracle.test.ts. This test will pass to demonstrate the described scenario.

    it("token0TVL is calculated to be 0 based on current implementation when calling BaseV1Router01.getPriceLP with unitReserves[i] being less than, equal to, or greater than but close to assetReserves[i]", async () => {
        // the Token0TVLCalculations contract simulates the current implementation and provides a suggested implementation for calculating token0TVL in BaseV1Router01.getPriceLP
        const Token0TVLCalculationsFactory = await ethers.getContractFactory("Token0TVLCalculations")
        const token0TVLCalculations = await Token0TVLCalculationsFactory.deploy()
        await token0TVLCalculations.deployed()

        // the calculateToken0TVLLt function simulates a situation where unitReserves[i] is less than assetReserves[i] when calling BaseV1Router01.getPriceLP for calculating token0TVL
        // token0TVLCurrent is the token0TVL value based on the current implementation in BaseV1Router01.getPriceLP
        // token0TVLSuggested is the token0TVL value based on the suggested implementation that can be implemented in BaseV1Router01.getPriceLP
        let [token0TVLCurrent, token0TVLSuggested] = await token0TVLCalculations.calculateToken0TVLLt()

        // token0TVL is 0 according to the current implementation
        expect(Number(token0TVLCurrent)).to.be.eq(0)

        // yet, token0TVL is bigger than 0 according to the suggested implementation
        expect(Number(token0TVLSuggested)).to.be.gt(0)

        // the calculateToken0TVLEq function simulates a situation where unitReserves[i] is equal to assetReserves[i] when calling BaseV1Router01.getPriceLP for calculating token0TVL
        // token0TVLCurrent is the token0TVL value based on the current implementation in BaseV1Router01.getPriceLP
        // token0TVLSuggested is the token0TVL value based on the suggested implementation that can be implemented in BaseV1Router01.getPriceLP
        ;[token0TVLCurrent, token0TVLSuggested] = await token0TVLCalculations.calculateToken0TVLEq()

        // token0TVL is 0 according to the current implementation
        expect(Number(token0TVLCurrent)).to.be.eq(0)

        // yet, token0TVL is bigger than 0 according to the suggested implementation
        expect(Number(token0TVLSuggested)).to.be.gt(0)

        // the calculateToken0TVLGc function simulates a situation where unitReserves[i] is greater than but close to assetReserves[i] when calling BaseV1Router01.getPriceLP for calculating token0TVL
        // token0TVLCurrent is the token0TVL value based on the current implementation in BaseV1Router01.getPriceLP
        // token0TVLSuggested is the token0TVL value based on the suggested implementation that can be implemented in BaseV1Router01.getPriceLP
        ;[token0TVLCurrent, token0TVLSuggested] = await token0TVLCalculations.calculateToken0TVLGc()

        // token0TVL is 0 according to the current implementation
        expect(Number(token0TVLCurrent)).to.be.eq(0)

        // yet, token0TVL is bigger than 0 according to the suggested implementation
        expect(Number(token0TVLSuggested)).to.be.gt(0)
    })

Tools Used

VSCode

In the getPriceLP function, https://github.com/code-423n4/2022-09-canto/blob/main/src/Swap/BaseV1-periphery.sol#L582 can be changed to the following code.

uint token0TVL = assetReserves[i] * prices[i] / decimals;

#0 - nivasan1

2022-09-08T21:26:54Z

duplicate of #41

[L-01] REQUESTED PERIOD AND WINDOW SIZES ARE HARDCODED

Currently, the number of periods requested is hardcoded to 8 and the window size is hardcoded to 1 in Swap\BaseV1-periphery.sol. In case these numbers need to be adjusted in the future, it can be beneficial to set up admin functions for adjusting these. Please consider adding these functions for controlling state variables that represent the requested period and window sizes and using these state variables to replace the following hardcoded numbers.

Swap\BaseV1-periphery.sol
  532: uint price = IBaseV1Pair(pair).quote(address(token), decimals, 8); // how much Canto is this asset worth?  
  544: uint price = IBaseV1Pair(pair).quote(address(token), decimals, 8);  
  562: (unitReserves, assetReserves) = pair.sampleReserves(8, 1);  
  566: (assetReserves, unitReserves) = pair.sampleReserves(8, 1);  
  572: (unitReserves, assetReserves) = pair.sampleReserves(8, 1);  
  576: (assetReserves, unitReserves) = pair.sampleReserves(8, 1);  
  581: for(uint i; i < 8; ++i) {
  586: uint LpPrice = LpPricesCumulative / 8; // take the average of the cumulative prices

[L-02] CONSTANTS CAN BE USED INSTEAD OF MAGIC NUMBERS

To improve readability and maintainability, constants can be used instead of magic numbers. Please consider replacing the magic numbers, such as 1e18, used in the following code with constants.

Swap\BaseV1-core.sol
  441: uint _x = x * 1e18 / decimals0; 
  442: uint _y = y * 1e18 / decimals1; 
  443: uint _a = (_x * _y) / 1e18; 
  444: uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18); 
  445: return _a * _b / 1e18;  // x3y+y3x >= k 

Swap\BaseV1-periphery.sol
  499: return 1e18; // Stable coins supported by the lending market are instantiated by governance and their price will always be 1 note   
  503: return 1e18 * 1e18 / (10 ** decimals); //Scale Price as a mantissa to maintain precision in comptroller   
  507: return 1e18 * 1e18 / (10 ** decimals); //Scale Price as a mantissa to maintain precision in comptroller   
  520: return getPriceCanto(underlying) * getPriceNote(address(wcanto), false) / 1e18;  
  533: return price * 1e18 / decimals; //return the scaled price   
  545: return price * 1e18 / decimals; // divide by decimals now to maintain precision   
  584: LpPricesCumulative += (token0TVL + token1TVL) * 1e18 / supply[i];   
  592: return LpPrice * getPriceNote(address(wcanto), false) / 1e18; // return the price in terms of Note  

[N-01] REDUNDANT CAST

In the following code, underlying() already returns an address type. Hence, ICErc20(address(ctoken)).underlying() does not need to be converted to address again.

Swap\BaseV1-periphery.sol
  495: underlying = address(ICErc20(address(ctoken)).underlying()); // We are getting the price for a CErc20 lending market

[N-02] MISSING NATSPEC COMMENTS

NatSpec comments provide rich code documentation. NatSpec comments are missing for the following functions. Please consider adding them.

Swap\BaseV1-core.sol
  137: function _update(uint balance0, uint balance1, uint _reserve0, uint _reserve1, uint _totalSupply) internal {
  201: function sample(address tokenIn, uint amountIn, uint points, uint window) public view returns (uint[] memory) {
  224: function reserves(uint granularity) external view returns(uint, uint) {
  237: function sampleReserves(uint points, uint window) public view returns (uint[] memory, uint[] memory) {
  260: function totalSupplyAvg(uint granularity) external view returns(uint) {
  271: function sampleSupply(uint points, uint window) public view returns (uint[] memory) {
  424: function _getAmountOut(uint amountIn, address tokenIn, uint _reserve0, uint _reserve1) internal view returns (uint) {

Swap\BaseV1-periphery.sol
  487: function getUnderlyingPrice(CToken ctoken) external override view returns(uint) {
  525: function getPriceCanto(address token_) internal view returns(uint) {
  537: function getPriceNote(address token_, bool stable) internal view returns(uint) {
  549: function getPriceLP(IBaseV1Pair pair) internal view returns(uint) {

[N-03] COMMENTED OUT CODE

Commented out code can cause confusion. If the following code are not used anymore, please remove them for readability and maintainability.

Swap\BaseV1-core.sol
  420: //amountIn -= amountIn / 10000; // remove fee from amount received

[N-04] uint256 CAN BE USED INSTEAD OF uint

Both uint and uint256 are used in Swap\BaseV1-core.sol and Swap\BaseV1-periphery.sol. For explicitness and consistency, please consider using uint256 instead uint in the following code.

Swap\BaseV1-core.sol
  137: function _update(uint balance0, uint balance1, uint _reserve0, uint _reserve1, uint _totalSupply) internal {
  138: uint blockTimestamp = block.timestamp;  
  139: uint timeElapsed = blockTimestamp - blockTimestampLast; 
  187: function quote(address tokenIn, uint amountIn, uint granularity) external view returns (uint amountOut) {
  188: uint [] memory _prices = sample(tokenIn, amountIn, granularity, 1);  
  189: uint priceAverageCumulative;  
  190: for (uint i = 0; i < _prices.length; i++) {  
  202: uint[] memory _prices = new uint[](points);  
  204: uint lastIndex = observations.length-1;  
  208: uint i = lastIndex - (points * window); // point from which to begin the sample  
  209: uint nextIndex = 0;  
  210: uint index = 0;  
  214: uint timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp;  
  216: uint _reserve1 = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed;  
  224: function reserves(uint granularity) external view returns(uint, uint) {
  225: (uint[] memory _reserves0, uint[] memory _reserves1)= sampleReserves(granularity, 1);  
  226: uint reserveAverageCumulative0;  
  227: uint reserveAverageCumulative1;  
  229: for (uint i = 0; i < _reserves0.length; ++i) {  
  237: function sampleReserves(uint points, uint window) public view returns (uint[] memory, uint[] memory) {
  238: uint[] memory _reserves0 = new uint[](points);  
  239: uint[] memory _reserves1 = new uint[](points);  
  241: uint lastIndex = observations.length-1;  
  243: uint i = lastIndex - (points * window); // point from which to begin the sample  
  244: uint nextIndex = 0;  
  245: uint index = 0;  
  246: uint timeElapsed;  
  260: function totalSupplyAvg(uint granularity) external view returns(uint) {
  261: uint[] memory _totalSupplyAvg = sampleSupply(granularity, 1);  
  262: uint totalSupplyCumulativeAvg;  
  264: for (uint i = 0; i < _totalSupplyAvg.length; ++i) {  
  271: function sampleSupply(uint points, uint window) public view returns (uint[] memory) {
  272: uint[] memory _totalSupply = new uint[](points);  
  274: uint lastIndex = observations.length-1;  
  276: uint i = lastIndex - (points * window); // point from which to begin the sample  
  277: uint nextIndex = 0;  
  278: uint index = 0;  
  279: uint timeElapsed;  

Swap\BaseV1-periphery.sol
  487: function getUnderlyingPrice(CToken ctoken) external override view returns(uint) {
  502: uint decimals = erc20(underlying).decimals();  
  506: uint decimals = erc20(underlying).decimals();  
  525: function getPriceCanto(address token_) internal view returns(uint) {
  531: uint decimals = 10 ** token.decimals(); // get decimals of token
  532: uint price = IBaseV1Pair(pair).quote(address(token), decimals, 8); // how much Canto is this asset worth?
  537: function getPriceNote(address token_, bool stable) internal view returns(uint) {
  543: uint decimals = 10 ** token.decimals(); 
  544: uint price = IBaseV1Pair(pair).quote(address(token), decimals, 8); 
  549: function getPriceLP(IBaseV1Pair pair) internal view returns(uint) {
  550: uint[] memory supply = pair.sampleSupply(8, 1); 
  551: uint[] memory prices;  
  552: uint[] memory unitReserves;  
  553: uint[] memory assetReserves;  
  556: uint decimals; 
  579: uint LpPricesCumulative; 
  581: for(uint i; i < 8; ++i) {
  582: uint token0TVL = assetReserves[i] * (prices[i] / decimals);
  583: uint token1TVL = unitReserves[i]; // price of the unit asset is always 1 
  586: uint LpPrice = LpPricesCumulative / 8; // take the average of the cumulative prices

[N-05] NEWER VERSION OF SOLIDITY CAN BE USED

The protocol can benefit from more fixes and gas-efficient features by using a newer version of Solidity. Changes for newer Solidity versions can be found in https://blog.soliditylang.org/category/releases/. Please consider updating the versions for the following files.

Swap\BaseV1-core.sol
  2: pragma solidity 0.8.11;

Swap\BaseV1-libs.sol
  1: pragma solidity 0.8.11;

Swap\BaseV1-periphery.sol
  3: pragma solidity 0.8.11;
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