Skip to content

Latest commit

 

History

History
291 lines (243 loc) · 11.7 KB

037.md

File metadata and controls

291 lines (243 loc) · 11.7 KB

Tiny Gingerbread Tarantula

High

Uninitialized amountCollateralPerPrinciple Array in matchOffersV3

Summary

The matchOffersV3 function in the DebitaV3Aggregator.sol contract initializes the amountCollateralPerPrinciple array but reads from it before assigning any values. This oversight leads to skewed calculations, particularly affecting updatedLastWeightAverage and updatedLastApr, same applies to amountPerPrinciple been read from here, and impacting core protocol logic.

Root Cause

The root cause is that the amountCollateralPerPrinciple array is initialized but not populated before being accessed. Specifically, m_amountCollateralPerPrinciple is set to zero by default since it reads from an uninitialized array. This zero value is then used in subsequent calculations, leading to incorrect results, while the documentation did not clearly defined the scope on how the updatedLastWeightAverage should be calculated as it uses the weightedAverageRatio[principleIndex] which was not initialized before the call, if amountCollateralPerPrinciple is properly initialized it will caused an error:

require(
                weightedAverageRatio[i] >=
                    ((ratiosForBorrower[i] * 9800) / 10000) && 
                    weightedAverageRatio[i] <=
                    (ratiosForBorrower[i] * 10200) / 10000, 
                "Invalid ratio"
            );

cause the weightedAverageRatio[i] will only be initialized for newWeightedAverage as updatedLastWeightAverage will remain 0.

weightedAverageRatio[principleIndex] =
                newWeightedAverage +
                updatedLastWeightAverage;

Because the updatedLastWeightAverage will always return 0, weightedAverageRatio will equals newWeightedAverage + 0 making

uint updatedLastWeightAverage = (weightedAverageRatio[
                principleIndex
            ] * m_amountCollateralPerPrinciple) /
                (m_amountCollateralPerPrinciple + userUsedCollateral);

on no-effect

Impact

Due to this issue, calculations for updatedLastWeightAverage and updatedLastApr become inaccurate. This will result in incorrect ratios and APRs within the loan matching process, potentially leading to unfair or invalid loan agreements. Specifically, the updatedLastWeightAverage will default to zero if m_amountCollateralPerPrinciple remains unassigned, which skews weighted average calculations and disrupts key protocol logic, and weightedAverageAPR will always results in newWeightedAPR which will also affect the requirement statement here

PoC

Mock PriceFeed

pragma solidity ^0.8.0;

contract MockPriceFeed {
    int256 private price;
    uint8 private decimals_;
    uint256 private roundId;
    uint256 private updatedAt;

    constructor(int256 _initialPrice, uint8 _decimals) {
        price = _initialPrice;
        decimals_ = _decimals;
        roundId = 1;
        updatedAt = block.timestamp;
    }

    function setPrice(int256 _price) external {
        price = _price;
        roundId++;
        updatedAt = block.timestamp;
    }

    function getRoundData(uint80 _roundId)
        external
        view
        returns (
            uint80,
            int256,
            uint256,
            uint256,
            uint80
        )
    {
        return (_roundId, price, updatedAt, updatedAt, _roundId);
    }

    function latestRoundData()
        external
        view
        returns (
            uint80,
            int256,
            uint256,
            uint256,
            uint80
        )
    {
        return (uint80(roundId), price, updatedAt, updatedAt, uint80(roundId));
    }
}
contract MockERC20Token is ERC20Mock {
    constructor() ERC20Mock() {}

    function mint_to(address account, uint256 amount) external {
        _mint(account, amount);
    }

    function decimals() public view virtual override returns (uint8) {
        return 6;
    }
}

contract DebitaAggregatorTest is Test, DynamicData {
    DBOFactory public DBOFactoryContract;
    DLOFactory public DLOFactoryContract;
    Ownerships public ownershipsContract;
    DebitaIncentives public incentivesContract;
    DebitaV3Aggregator public DebitaV3AggregatorContract;
    auctionFactoryDebita public auctionFactoryDebitaContract;
    DynamicData public allDynamicData;

    MockPriceFeed public AEROPriceFeed;
    MockPriceFeed public USDPriceFeed;
    DebitaChainlink public chainlink;

    DLOImplementation public LendOrder;
    DBOImplementation public BorrowOrder;
    ERC20Mock public AEROContract;
    address AERO;
    MockERC20Token USD;
    // address owner
    address owner = address(0x011111);
    address alice = address(0x0222222);
    address bob = address(0x0333333);


    function setUp() public {
        allDynamicData = new DynamicData();
        ownershipsContract = new Ownerships();
        incentivesContract = new DebitaIncentives();
        DBOImplementation borrowOrderImplementation = new DBOImplementation();
        DBOFactoryContract = new DBOFactory(address(borrowOrderImplementation));
        DLOImplementation proxyImplementation = new DLOImplementation();
        DLOFactoryContract = new DLOFactory(address(proxyImplementation));
        auctionFactoryDebitaContract = new auctionFactoryDebita();

        AEROPriceFeed = new MockPriceFeed(120000000, 8); // Price of 1 AERO in USD is 1.2
        USDPriceFeed = new MockPriceFeed(100000000, 8); // Price of 1 USD in USD is 1
        chainlink = new DebitaChainlink(address(0x0), address(this));

        AEROContract = new ERC20Mock();
        USD = new MockERC20Token();
        AERO = address(AEROContract);

        chainlink.setPriceFeeds(AERO, address(AEROPriceFeed));
        chainlink.setPriceFeeds(address(USD), address(USDPriceFeed));

        DebitaV3Loan loanInstance = new DebitaV3Loan();
        DebitaV3AggregatorContract = new DebitaV3Aggregator(
            address(DLOFactoryContract),
            address(DBOFactoryContract),
            address(incentivesContract),
            address(ownershipsContract),
            address(auctionFactoryDebitaContract),
            address(loanInstance)
        );

        DebitaV3AggregatorContract.setOracleEnabled(address(chainlink), true);

        ownershipsContract.setDebitaContract(
            address(DebitaV3AggregatorContract)
        );
        auctionFactoryDebitaContract.setAggregator(
            address(DebitaV3AggregatorContract)
        );
        DLOFactoryContract.setAggregatorContract(
            address(DebitaV3AggregatorContract)
        );
        DBOFactoryContract.setAggregatorContract(
            address(DebitaV3AggregatorContract)
        );

        incentivesContract.setAggregatorContract(
            address(DebitaV3AggregatorContract)
        );

        setupOrders();
    }
    
    function testUserUsedCollateralCalculation() public {
        // Set up test data
        address[] memory lendOrders = new address[](1);
        uint[] memory lendAmountPerOrder = new uint[](1);
        uint[] memory porcentageOfRatioPerLendOrder = new uint[](1);
        address[] memory principles = new address[](1);
        uint[] memory indexForPrinciple_BorrowOrder = new uint[](1);
        uint[] memory indexForCollateral_LendOrder = new uint[](1);
        uint[] memory indexPrinciple_LendOrder = new uint[](1);

        lendOrders[0] = address(LendOrder);
        lendAmountPerOrder[0] = 100 * 10 ** 6; 
        porcentageOfRatioPerLendOrder[0] = 10000;
        principles[0] = address(USD);
        indexForPrinciple_BorrowOrder[0] = 0;
        indexForCollateral_LendOrder[0] = 0;
        indexPrinciple_LendOrder[0] = 0;

        uint principleDecimals = IERC20Metadata(principles[0]).decimals();
        uint collateralDecimals = IERC20Metadata(address(AERO)).decimals();
        uint expectedUserUsedCollateral = (lendAmountPerOrder[0] * (10 ** collateralDecimals)) / (10 ** principleDecimals);

        address loan = DebitaV3AggregatorContract.matchOffersV3(
            lendOrders, lendAmountPerOrder, porcentageOfRatioPerLendOrder,
            address(BorrowOrder), principles, indexForPrinciple_BorrowOrder,
            indexForCollateral_LendOrder, indexPrinciple_LendOrder
        );

        DebitaV3Loan loanContract = DebitaV3Loan(loan);
        DebitaV3Loan.infoOfOffers[] memory offers = loanContract.getLoanData()._acceptedOffers;
        uint actualUserUsedCollateral = offers[0].collateralUsed;
    }


    function setupOrders() internal {
        USD.mint_to(alice, 1000e18);
        USD.mint_to(bob, 1000e18);
        deal(address(AEROContract), alice, 1000e18, true);
        deal(address(AEROContract), bob, 1000e18, true);

        bool[] memory oraclesActivated = allDynamicData.getDynamicBoolArray(1);
        uint[] memory ltvs = allDynamicData.getDynamicUintArray(1);
        uint[] memory ratio = allDynamicData.getDynamicUintArray(1);

        address[] memory acceptedPrinciples = allDynamicData
            .getDynamicAddressArray(1);
        address[] memory oraclesPrinciples = allDynamicData
            .getDynamicAddressArray(1);

        ratio[0] = 1e18;
        oraclesPrinciples[0] = address(chainlink);
        acceptedPrinciples[0] = address(USD);
        oraclesActivated[0] = true;
        ltvs[0] = 10000;

        vm.startPrank(bob);
        IERC20(AERO).approve(address(DBOFactoryContract), 1000e18);
        address borrowOrderAddress = DBOFactoryContract.createBorrowOrder(
            oraclesActivated,
            ltvs,
            10000,
            864000,
            acceptedPrinciples,
            AERO,
            false,
            0,
            oraclesPrinciples,
            ratio,
            address(chainlink),
            500e18
        );

        vm.startPrank(alice);
        IERC20(USD).approve(address(DLOFactoryContract), 1000e18);
        address[] memory acceptedLendCollateral = allDynamicData
            .getDynamicAddressArray(1);
        acceptedLendCollateral[0] = address(AERO);

        address lendOrderAddress = DLOFactoryContract.createLendOrder(
            false,
            oraclesActivated,
            false,
            ltvs,
            10000,
            8640000,
            86400,
            acceptedLendCollateral,
            address(USD),
            oraclesPrinciples,
            ratio,
            address(chainlink),
            500e18
        );

        LendOrder = DLOImplementation(lendOrderAddress);
        BorrowOrder = DBOImplementation(borrowOrderAddress);
    }
}

Screenshot 2024-11-15 at 09 37 15

Mitigation

Ensure there is proper initializations of values that affects the logics to calculate