Contract 0x9AC6a2cdA5401DebE62746F1B8184B6C4d0801aD

Txn Hash Method
Block
From
To
Value [Txn Fee]
0xe592d8f5742707235f9491e7775913eafe7d7b4b6fd6ad6bdb28b8a7703240240x60806040227543652022-11-23 20:33:134 days 17 hrs ago0x000000000e8bb18028d78047a15f79ed3511c565 IN  Create: TroveManager0 AVAX0.226643805 45
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
TroveManager

Compiler Version
v0.6.11+commit.5ef660b1

Optimization Enabled:
Yes with 110 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 25 : TroveManager.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "../Interfaces/ITroveManager.sol";
import "../Interfaces/ISortedTroves.sol";
import "../Interfaces/IYetiController.sol";
import "../Interfaces/ITroveManagerLiquidations.sol";
import "../Interfaces/ITroveManagerRedemptions.sol";
import "../Interfaces/IERC20.sol";
import "../Dependencies/TroveManagerBase.sol";
import "../Dependencies/ReentrancyGuardUpgradeable.sol";
import "../Interfaces/IBorrowerOperations.sol";

// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&   ,[email protected]@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@&&&.,,      ,,**.&&&&&@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@,               ..,,,,,,,,,&@@@@@@@@@@
// @@@@@@,,,,,,&@@@@@@@@&                       ,,,,,&@@@@@@@@@
// @@@&,,,,,,,,@@@@@@@@@                        ,,,,,*@@@/@@@@@
// @@,*,*,*,*#,,*,&@@@@@   $$          $$       *,,,  ***&@@@@@
// @&***********(@@@@@@&   $$          $$       ,,,%&. & %@@@@@
// @(*****&**     &@@@@#                        *,,%  ,#%@*&@@@
// @... &             &                         **,,*&,(@*,*,&@
// @&,,.              &                         *,*       **,,@
// @@@,,,.            *                         **         ,*,,
// @@@@@,,,...   .,,,,&                        .,%          *,*
// @@@@@@@&/,,,,,,,,,,,,&,,,,,.         .,,,,,,,,.           *,
// @@@@@@@@@@@@&&@(,,,,,(@&&@@&&&&&%&&&&&%%%&,,,&            .(
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&,,,,,,,,,,,,,,&             &
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/,,,,,,,,,,,,&             &
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/            &             &
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&              &             &
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&      ,,,@@@&  &  &&  .&( &#%
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&&&&&%#**@@@&*&*******,,,,,**
//
//  $$\     $$\          $$\     $$\       $$$$$$$$\ $$\                                                   
//  \$$\   $$  |         $$ |    \__|      $$  _____|\__|                                                  
//   \$$\ $$  /$$$$$$\ $$$$$$\   $$\       $$ |      $$\ $$$$$$$\   $$$$$$\  $$$$$$$\   $$$$$$$\  $$$$$$\  
//    \$$$$  /$$  __$$\\_$$  _|  $$ |      $$$$$\    $$ |$$  __$$\  \____$$\ $$  __$$\ $$  _____|$$  __$$\ 
//     \$$  / $$$$$$$$ | $$ |    $$ |      $$  __|   $$ |$$ |  $$ | $$$$$$$ |$$ |  $$ |$$ /      $$$$$$$$ |
//      $$ |  $$   ____| $$ |$$\ $$ |      $$ |      $$ |$$ |  $$ |$$  __$$ |$$ |  $$ |$$ |      $$   ____|
//      $$ |  \$$$$$$$\  \$$$$  |$$ |      $$ |      $$ |$$ |  $$ |\$$$$$$$ |$$ |  $$ |\$$$$$$$\ \$$$$$$$\ 
//      \__|   \_______|  \____/ \__|      \__|      \__|\__|  \__| \_______|\__|  \__| \_______| \_______|

/**
 * @title Deals with state of all system troves
 * @notice It has all the external functions for liquidations, redemptions,
 * as well as functions called by BorrowerOperations function calls.
 */

contract TroveManager is TroveManagerBase, ITroveManager, ReentrancyGuardUpgradeable {
    address internal borrowerOperationsAddress;

    ITroveManager internal troveManager;

    ITroveManagerRedemptions internal troveManagerRedemptions;

    ITroveManagerLiquidations internal troveManagerLiquidations;

    ISortedTroves internal sortedTroves;


    string public constant NAME = "TroveManager";

    // --- Data structures ---

    uint256 internal constant SECONDS_IN_ONE_MINUTE = 60;

    /*
     * Half-life of 12h. 12h = 720 min
     * (1/2) = d^720 => d = (1/2)^(1/720)
     */
    uint256 public constant MINUTE_DECAY_FACTOR = 999037758833783000;
    uint256 public constant MAX_BORROWING_FEE = DECIMAL_PRECISION * 5 / 100; // 5%

    // During bootsrap period redemptions are not allowed
    uint256 constant BOOTSTRAP_PERIOD = 14 days;

    // See documentation for explanation of baseRate
    uint256 public baseRate;

    // The timestamp of the latest fee operation (redemption or new YUSD issuance)
    uint256 public lastFeeOperationTime;

    // Mapping of all troves in the system
    mapping(address => Trove) Troves;

    // Total stakes keeps track of the sum of all stakes for each collateral, across all users. 
    mapping(address => uint256) totalStakes;

    // Snapshot of the value of totalStakes, taken immediately after the latest liquidation
    mapping(address => uint256) public totalStakesSnapshot;

    // Snapshot of the total collateral across the ActivePool and DefaultPool, immediately after the latest liquidation.
    mapping(address => uint256) public totalCollateralSnapshot;

    /*
     * L_Coll and L_YUSDDebt track the sums of accumulated liquidation rewards per unit staked. Each collateral type has
     * its own L_Coll and L_YUSDDebt.
     * During its lifetime, each stake earns:
     *
     * A Collateral gain of ( stake * [L_Coll[coll] - L_Coll[coll](0)] )
     * A YUSDDebt increase  of ( stake * [L_YUSDDebt[coll] - L_YUSDDebt[coll](0)] )
     *
     * Where L_Coll[coll](0) and L_YUSDDebt[coll](0) are snapshots of L_Coll[coll] and L_YUSDDebt[coll]
     * for the active Trove taken at the instant the stake was made
     */
    mapping(address => uint256) L_Coll;
    mapping(address => uint256) L_YUSDDebt;

    // Map addresses with active troves to their RewardSnapshot
    mapping(address => RewardSnapshot) rewardSnapshots;

    // Object containing the reward snapshots for a given active trove
    struct RewardSnapshot {
        mapping(address => uint256) CollRewards;
        mapping(address => uint256) YUSDDebts;
    }

    // Array of all active trove addresses - used to to compute an approximate hint off-chain, for the sorted list insertion
    address[] TroveOwners;

    // Error trackers for the trove redistribution calculation
    mapping(address => uint256) public lastCollError_Redistribution;
    mapping(address => uint256) public lastYUSDDebtError_Redistribution;

    /*
     * --- Variable container structs for liquidations ---
     *
     * These structs are used to hold, return and assign variables inside the liquidation functions,
     * in order to avoid the error: "CompilerError: Stack too deep".
     **/

    // --- Events ---

    event BaseRateUpdated(uint256 _baseRate);
    event LastFeeOpTimeUpdated(uint256 _lastFeeOpTime);
    event TotalStakesUpdated(address token, uint256 _newTotalStakes);
    event SystemSnapshotsUpdated(uint256 _unix);

    event Liquidation(
        uint256 liquidatedAmount,
        uint256 totalYUSDGasCompensation,
        address[] totalCollTokens,
        uint256[] totalCollAmounts,
        address[] totalCollGasCompTokens,
        uint256[] totalCollGasCompAmounts
    );

    event LTermsUpdated(address _Coll_Address, uint256 _L_Coll, uint256 _L_YUSDDebt);
    event TroveSnapshotsUpdated(uint256 _unix);
    event TroveIndexUpdated(address _borrower, uint256 _newIndex);
    event TroveUpdated(
        address indexed _borrower,
        uint256 _debt,
        address[] _tokens,
        uint256[] _amounts,
        TroveManagerOperation operation
    );

    bool addressSet;

    // =============== New variables for Interest Rates ===============

    // Interest rates will be updated using a similar system to the current redistribution stakes
    // system. At some time t_0, the global L_YUSDInterest[coll] will be some value L_YUSDInterest[coll](0)
    // When a user opens their trove, their snapshot interestReward[user][coll] will be set to
    // L_YUSDInterest[coll](0). At some future time t_1, we will log an increase of debtIncrease =
    // debt * ( ( L_YUSDDebt[coll](1) - L_YUSDDebt[coll](0) ) / L_YUSDDebt[coll](0) + 1 ).
    // For example, if L_YUSDInterest[coll](0) = 1.05, the yearly interest rate is 2%, and one year passes, then
    // L_YUSDInterest[coll](1) = 1.071. debtIncrease = debt * ((1.071 - 1.05) / 1.05 + 1) = debt * 1.02. 

    // Previously: L_YUSDDebt mapping (address of collateral => uint of accumulated liquidation reward per unit staked)
    // New: L_YUSDInterest (address of collateral => uint of proportional increase in the accumulated interest)
    mapping (address => uint256) L_YUSDInterest;

    // Struct to keep track of interest information
    struct InterestSnapshot {
        // Previously: RewardSnapshot.YUSDDebts mapping (address of collateral => uint of reward snapshot at last adjustment 
        //  for the user, for YUSD debts)
        // New: interestReward mapping (address of collateral => uint of interest reward snapshot at last adjustment 
        //  for the user, for interest)
        mapping (address => uint256) interestReward;

        // Used to calculate proportion of interest per collateral for each user. Their overall interest rate is a weighted sum
        //  based on the trove composition. This term represents the weights, summing to 1e18. 
        mapping (address => uint256) collateralProportion;

        // Sum of all the collateralProportion
        uint256 collateralProportionDenominator;
    }

    mapping (address => InterestSnapshot) interestSnapshots;

    uint256 public lastInterestRateUpdateTime;

    uint256 public interestTimeWindow;

    event L_YUSDInterestUpdated(address token, uint256 new_Lterm);

    event InterestApplied(address _borrower, uint256 totalInterest);

    // Commented out for contract space
    // function setAddresses(
    //     address _borrowerOperationsAddress,
    //     address _activePoolAddress,
    //     address _defaultPoolAddress,
    //     address _sortedTrovesAddress,
    //     address _controllerAddress,
    //     address _troveManagerRedemptionsAddress,
    //     address _troveManagerLiquidationsAddress
    // ) external override {
    //     require(addressSet == false, "Addresses already set");
    //     addressSet = true;
    //     __ReentrancyGuard_init();

    //     borrowerOperationsAddress = _borrowerOperationsAddress;
    //     activePool = IActivePool(_activePoolAddress);
    //     defaultPool = IDefaultPool(_defaultPoolAddress);
    //     controller = IYetiController(_controllerAddress);
    //     sortedTroves = ISortedTroves(_sortedTrovesAddress);
    //     troveManagerRedemptions = ITroveManagerRedemptions(_troveManagerRedemptionsAddress);
    //     troveManagerLiquidations = ITroveManagerLiquidations(_troveManagerLiquidationsAddress);
    // }

    // --- Trove Liquidation functions ---

    /**
     * @notice Single liquidation function. Closes the trove if its ICR is lower than the minimum collateral ratio.
     * @param _borrower The address of the Trove owner
     */
    function liquidate(address _borrower) external override nonReentrant {
        _requireTroveIsActive(_borrower);

        address[] memory borrowers = new address[](1);
        borrowers[0] = _borrower;
        _callBatchLiquidateTroves(borrowers, msg.sender);
    }

    /**
     * @notice Attempt to liquidate a custom list of troves provided by the caller.
     * @param _troveArray The list of Troves' Addresses
     * @param _liquidator The address of the liquidator 
     */
    function batchLiquidateTroves(address[] memory _troveArray, address _liquidator)
        external
        override
        nonReentrant
    {
        _callBatchLiquidateTroves(_troveArray, _liquidator);
    }

    // --- Liquidation helper functions ---

    /**
     * @notice Update position for a set of troves using latest price data. This can be called by anyone.
     * Yeti Finance will also be running a bot to assist with keeping the list from becoming too stale.
     * @param _borrowers The list of addresses of the troves to update
     * @param _lowerHints The list of lower hints for the troves which are to be updated
     * @param _upperHints The list of upper hints for the troves which are to be updated
     */
    function updateTroves(
        address[] calldata _borrowers,
        address[] calldata _lowerHints,
        address[] calldata _upperHints
    ) external override {
        uint256 lowerHintsLen = _lowerHints.length;
        _revertLenInput(_borrowers.length == lowerHintsLen && lowerHintsLen == _upperHints.length);

        uint256[] memory AICRList = new uint256[](lowerHintsLen);

        _tickInterest(false);

        for (uint256 i; i < lowerHintsLen; ++i) {
            (
                address[] memory tokens,
                uint256[] memory amounts, 
                uint256 debt
            ) = _getCurrentTroveState(_borrowers[i]);
            AICRList[i] = _getAICR(tokens, amounts, debt);
        }
        sortedTroves.reInsertMany(_borrowers, AICRList, _lowerHints, _upperHints);
    }

    /**
     * @notice Update a particular trove address in the underCollateralized troves list
     * @dev This function is called by the UpdateTroves bot if there are many underCollateralized troves
     * during congested network conditions where potentially it is tough to liquidate them all.
     * In this case, the function adds to the underCollateralizedTroves list so no SP withdrawal can happen.
     * If the trove is no longer underCollateralized then this function will remove
     * it from the list. This function calls sortedTroves' updateUnderCollateralizedTrove function.
     * Intended to be a cheap function call since it is going to be called when liquidations are not possible
     * @param _ids Trove ids
     */
    function updateUnderCollateralizedTroves(address[] calldata _ids) external override {
        for (uint i; i < _ids.length; ++i) {
            // If ICR < MCR, is undercollateralized
            _updateUnderCollateralizedTrove(_ids[i], getCurrentICR(_ids[i]) < MCR);
        }
    }

    /**
     * @notice Send _YUSDamount YUSD to the system and redeem the corresponding amount of collateral
     * from as many Troves as are needed to fill the redemption request. Applies pending rewards to a Trove before reducing its debt and coll.
     * @dev if _amount is very large, this function can run out of gas, specially if traversed troves are small. This can be easily avoided by
     * splitting the total _amount in appropriate chunks and calling the function multiple times.
     *
     * Param `_maxIterations` can also be provided, so the loop through Troves is capped (if it’s zero, it will be ignored).This makes it easier to
     * avoid OOG for the frontend, as only knowing approximately the average cost of an iteration is enough, without needing to know the “topology”
     * of the trove list. It also avoids the need to set the cap in stone in the contract, nor doing gas calculations, as both gas price and opcode
     * costs can vary.
     *
     * All Troves that are redeemed from -- with the likely exception of the last one -- will end up with no debt left, therefore they will be closed.
     * If the last Trove does have some remaining debt, it has a finite ICR, and the reinsertion could be anywhere in the list, therefore it requires a hint.
     * A frontend should use getRedemptionHints() to calculate what the ICR of this Trove will be after redemption, and pass a hint for its position
     * in the sortedTroves list along with the ICR value that the hint was found for.
     *
     * If another transaction modifies the list between calling getRedemptionHints() and passing the hints to redeemCollateral(), it is very
     * likely that the last (partially) redeemed Trove would end up with a different ICR than what the hint is for. In this case the redemption
     * will stop after the last completely redeemed Trove and the sender will keep the remaining YUSD amount, which they can attempt to redeem later.
     * @param _YUSDamount The intended amount of YUSD to redeem
     * @param _YUSDMaxFee The maximum accepted fee in YUSD the user is willing to pay
     * @param _firstRedemptionHint The hint for the position of the first redeemed Trove in the sortedTroves list
     * @param _upperPartialRedemptionHint The upper hint for the position of the last partially redeemed Trove in the sortedTroves list
     * @param _lowerPartialRedemptionHint The lower hint for the position of the last partially redeemed Trove in the sortedTroves list
     * @param _partialRedemptionHintAICR The AICR of the last partially redeemed Trove in the sortedTroves list
     * @param _maxIterations The maximum number of iterations to perform. If zero, the function will run until it runs out of gas.
     */
    function redeemCollateral(
        uint256 _YUSDamount,
        uint256 _YUSDMaxFee,
        address _firstRedemptionHint,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintAICR,
        uint256 _maxIterations
    ) external override nonReentrant {
        troveManagerRedemptions.redeemCollateral(
            _YUSDamount,
            _YUSDMaxFee,
            _firstRedemptionHint,
            _upperPartialRedemptionHint,
            _lowerPartialRedemptionHint,
            _partialRedemptionHintAICR,
            _maxIterations,
            msg.sender
        );
    }

    /** 
     * @notice Secondary function for redeeming collateral. See above for how YUSDMaxFee is calculated.
            Redeems one collateral type from only one trove. Included for gas efficiency of arbitrages. 
     * @param _YUSDamount is equal to the amount of YUSD to actually redeem. 
     * @param _YUSDMaxFee is equal to the max fee in YUSD that the sender is willing to pay
     * @param _target is the hint for the single trove to redeem against
     * @param _upperHint is the upper hint for reinsertion of the trove
     * @param _lowerHint is the lower hint for reinsertion of the trove
     * @param _hintAICR is the target hint AICR for the the trove redeemed
     * @param _collToRedeem is the collateral address to redeem. Only this token.
     * _YUSDamount + _YUSDMaxFee must be less than the balance of the sender.
     */
    function redeemCollateralSingle(
        uint256 _YUSDamount,
        uint256 _YUSDMaxFee,
        address _target,
        address _upperHint,
        address _lowerHint,
        uint256 _hintAICR,
        address _collToRedeem
    ) external override nonReentrant {
        troveManagerRedemptions.redeemCollateralSingle(
            _YUSDamount,
            _YUSDMaxFee,
            _target,
            _upperHint,
            _lowerHint,
            _hintAICR,
            _collToRedeem,
            msg.sender
        );
    }

    // --- Getters ---

    function getTroveOwnersCount() external view override returns (uint256) {
        return TroveOwners.length;
    }

    function getTroveFromTroveOwnersArray(uint256 _index) external view override returns (address) {
        return TroveOwners[_index];
    }

    // --- Helper functions ---

    /**
     * @notice Helper function to return the current individual collateral ratio (ICR) of a given Trove.
     * @dev Takes a trove's pending coll and debt rewards from redistributions into account.
     * @param _borrower The address of the Trove to get the ICR
     * @return ICR
     */
    function getCurrentICR(address _borrower) public view override returns (uint256 ICR) {
        (address[] memory tokens, uint256[] memory amounts, uint256 currentYUSDDebt) = _getCurrentTroveState(_borrower);

        ICR = _getICR(tokens, amounts, currentYUSDDebt);
    }

    /**
     *   @notice Helper function to return the current recovery individual collateral ratio (AICR) of a given Trove.
     *           AICR uses recovery ratios which are higher for more stable assets like stablecoins.
     *   @dev Takes a trove's pending coll and debt rewards from redistributions into account.
     *   @param _borrower The address of the Trove to get the AICR
     *   @return AICR
     */
    function getCurrentAICR(address _borrower) external view override returns (uint256 AICR) {
        (address[] memory tokens, uint256[] memory amounts, uint256 currentYUSDDebt) = _getCurrentTroveState(_borrower);

        AICR = _getAICR(tokens, amounts, currentYUSDDebt);
    }

    /**
     *   @notice Gets current trove state as colls and debt.
     *   @param _borrower The address of the Trove
     *   @return colls -- newColls of the trove tokens and amounts
     *   @return YUSDdebt -- the current debt of the trove
     */
    function _getCurrentTroveState(address _borrower)
        internal
        view
        returns (address[] memory, uint256[] memory, uint256)
    {
        newColls memory pendingCollReward = _getPendingCollRewards(_borrower);
        (uint256 pendingYUSDDebtReward, uint256 pendingYUSDInterest) = getPendingYUSDDebtReward(_borrower);

        uint256 YUSDdebt = Troves[_borrower].debt.add(pendingYUSDDebtReward).add(pendingYUSDInterest);
        newColls memory colls = _sumColls(Troves[_borrower].colls, pendingCollReward);
        return (colls.tokens, colls.amounts, YUSDdebt);
    }

    /**
     * @notice Add the borrowers's coll and debt rewards earned from redistributions, to their Trove. 
     * Also applies pending interest after the interest rate
     * @param _borrower The address of the Trove
     */
    function applyPendingRewards(address _borrower) external override {
        // Only actually called by BO or TMR
        _requireCallerIsBOorTMRorTML();
        return _applyPendingRewards(activePool, defaultPool, _borrower);
    }

    /**
     * @notice Add the borrowers's coll and debt rewards earned from redistributions, to their Trove
     * Also applies pending interest after the interest rate
     * @param _borrower The address of the Trove
     */
    function _applyPendingRewards(
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        address _borrower
    ) internal {
        // Check if interest has to be ticked before interaction
        _tickInterest(false);

        if (hasPendingRewards(_borrower)) {
            _requireTroveIsActive(_borrower);

            // Compute pending collateral rewards
            newColls memory pendingCollReward = _getPendingCollRewards(_borrower);
            (uint256 pendingYUSDDebtReward, uint256 pendingYUSDInterest) = getPendingYUSDDebtReward(_borrower);

            // Apply pending rewards to trove's state
            Troves[_borrower].colls = _sumColls(Troves[_borrower].colls, pendingCollReward);
            Troves[_borrower].debt = Troves[_borrower].debt.add(pendingYUSDDebtReward).add(pendingYUSDInterest);

            // Mint the YUSD to the fee recipient
            IBorrowerOperations(borrowerOperationsAddress).mintYUSDInterestFee(pendingYUSDInterest);

            _updateTroveRewardSnapshots(_borrower);

            // Move pending rewards to active and default pool
            _defaultPool.decreaseYUSDDebt(pendingYUSDDebtReward);
            _activePool.increaseYUSDDebt(pendingYUSDDebtReward.add(pendingYUSDInterest));
            _defaultPool.sendCollsToActivePool(pendingCollReward.tokens, pendingCollReward.amounts);

            emit InterestApplied(_borrower, pendingYUSDInterest);

            emit TroveUpdated(
                _borrower,
                Troves[_borrower].debt,
                Troves[_borrower].colls.tokens,
                Troves[_borrower].colls.amounts,
                TroveManagerOperation.applyPendingRewards
            );
        }
    }

    /**
     * @notice Update borrower's snapshots of L_Coll and L_YUSDDebt to reflect the current values
     * @param _borrower The address of the Trove
     */
    function updateTroveRewardSnapshots(address _borrower) external override {
        _requireCallerIsBorrowerOperations();
        // Check if interest has to be ticked before interaction
        _tickInterest(false);
        _updateTroveRewardSnapshots(_borrower);
    }

    /**
     * @notice Internal function to update borrower's snapshots of L_Coll and L_YUSDDebt to reflect the current values
     *         Called when updating trove reward snapshots or when applying pending rewards
     * @param _borrower The address of the Trove
     */
    function _updateTroveRewardSnapshots(address _borrower) internal {
        address[] memory allColls = Troves[_borrower].colls.tokens;

        InterestSnapshot storage interestSnapshotUser = interestSnapshots[_borrower];

        for (uint256 i; i < allColls.length; ++i) {
            address asset = allColls[i];
            rewardSnapshots[_borrower].CollRewards[asset] = L_Coll[asset];
            rewardSnapshots[_borrower].YUSDDebts[asset] = L_YUSDDebt[asset];

            // NEW INTEREST RATE UPDATE
            interestSnapshotUser.interestReward[asset] = L_YUSDInterest[asset];
        }
        emit TroveSnapshotsUpdated(block.timestamp);
    }

    /**
     * @notice Get the borrower's pending accumulated Coll rewards, earned by their stake
     * @dev Returned tokens and amounts are the length of controller.getValidCollateral()
     * @param _borrower The address of the Trove
     * @return The borrower's pending accumulated Coll rewards tokens
     * @return The borrower's pending accumulated Coll rewards amounts
     */
    function getPendingCollRewards(address _borrower)
        external
        view
        override
        returns (address[] memory, uint256[] memory)
    {
        newColls memory pendingCollRewards = _getPendingCollRewards(_borrower);
        return (pendingCollRewards.tokens, pendingCollRewards.amounts);
    }

    /**
     * @notice Get the borrower's pending accumulated Coll rewards, earned by their stake
     * @param _borrower The address of the Trove
     * @return pendingCollRewards 
     */
    function _getPendingCollRewards(address _borrower)
        internal
        view
        returns (newColls memory pendingCollRewards)
    {
        if (Troves[_borrower].status != Status.active) {
            newColls memory emptyColls;
            return emptyColls;
        }

        address[] memory allColls = Troves[_borrower].colls.tokens;
        uint256 allCollsLen = allColls.length;
        pendingCollRewards.amounts = new uint256[](allCollsLen);
        pendingCollRewards.tokens = allColls;
        for (uint256 i; i < allCollsLen; ++i) {
            address coll = allColls[i];
            uint256 snapshotCollReward = rewardSnapshots[_borrower].CollRewards[coll];
            uint256 rewardPerUnitStaked = L_Coll[coll].sub(snapshotCollReward);
            if (rewardPerUnitStaked == 0) {
                pendingCollRewards.amounts[i] = 0;
                continue;
            }

            // new assetCollReward = stake * reward per unit staked div 10**decimals
            pendingCollRewards.amounts[i] = Troves[_borrower].stakes[coll].mul(rewardPerUnitStaked).div(10**_getIERC20Decimals(coll));
        }
    }

    /**
     * @notice : Get the borrower's pending accumulated YUSD reward, earned by their stake
     *  Additionally, after interest rate update, pending accumulated YUSD can be increased
     *  by per-collateral interest. 
     * @param _borrower The address of the Trove
     */
    function getPendingYUSDDebtReward(address _borrower)
        public
        view
        override
        returns (uint256 pendingYUSDDebtReward, uint256 pendingYUSDInterest)
    {
        if (Troves[_borrower].status != Status.active) {
            return (0, 0);
        }
        address[] storage allColls = Troves[_borrower].colls.tokens;

        // Apply on initial debt
        uint256 initialDebt = Troves[_borrower].debt.sub(YUSD_GAS_COMPENSATION);
        uint256 summedInterest = 0;
        InterestSnapshot storage userInterestSnapshot = interestSnapshots[_borrower];

        for (uint256 i; i < allColls.length; ++i) {
            address coll = allColls[i];

            {
                // NEW INTEREST RATE UPDATE
                uint256 interestSnapshot = userInterestSnapshot.interestReward[coll];
                uint256 debtIncrease = (L_YUSDInterest[coll].sub(interestSnapshot));
                if (debtIncrease != 0 && interestSnapshot != 0) {
                    uint256 assetInterest = userInterestSnapshot.collateralProportion[coll].mul(debtIncrease).div(interestSnapshot);
                    summedInterest = summedInterest.add(assetInterest);
                }
            }

            uint256 snapshotYUSDDebt = rewardSnapshots[_borrower].YUSDDebts[coll];
            uint256 rewardPerUnitStaked = L_YUSDDebt[coll].sub(snapshotYUSDDebt);
            if (rewardPerUnitStaked == 0) {
                continue;
            }

            uint256 stake = Troves[_borrower].stakes[coll];
            uint256 assetYUSDDebtReward = stake.mul(rewardPerUnitStaked).div(10**_getIERC20Decimals(coll));
            pendingYUSDDebtReward = pendingYUSDDebtReward.add(assetYUSDDebtReward);
        }

        if (summedInterest != 0) {
            pendingYUSDInterest = summedInterest.mul(initialDebt).div(userInterestSnapshot.collateralProportionDenominator);
        }
    }

    /**
     * @notice Checks if borrower has pending rewards
     * @dev A Trove has pending rewards if its snapshot is less than the current rewards per-unit-staked sum:
     * this indicates that rewards have occured since the snapshot was made, and the user therefore has pending rewards
     * @param _borrower The address of the Trove
     * @return True if Trove has pending rewards, False if Trove doesn't have pending rewards
     */
    function hasPendingRewards(address _borrower) public view override returns (bool) {
        if (Troves[_borrower].status != Status.active) {
            return false;
        }
        address[] storage assets = Troves[_borrower].colls.tokens;
        for (uint256 i; i < assets.length; ++i) {
            address token = assets[i];
            // NEW INTEREST RATE UPDATE: Check also for if there is a pending interest reward
            if (rewardSnapshots[_borrower].CollRewards[token] < L_Coll[token] 
                || interestSnapshots[_borrower].interestReward[token] < L_YUSDInterest[token]) {
                return true;
            }
        }
        return false;
    }

    /**
     * @notice Gets the entire debt and collateral of a borrower 
     * @param _borrower The address of the Trove
     * @return debt, collsTokens, collsAmounts, pendingYUSDDebtReward, pendingRewardTokens, pendingRewardAmouns
     */
    function getEntireDebtAndColls(address _borrower)
        external
        view
        override
        returns (
            uint256,
            address[] memory,
            uint256[] memory,
            uint256,
            address[] memory,
            uint256[] memory,
            uint256
        )
    {
        uint256 debt = Troves[_borrower].debt;
        newColls memory colls = Troves[_borrower].colls;

        (uint256 pendingYUSDDebtReward, uint256 pendingYUSDInterest) = getPendingYUSDDebtReward(_borrower);
        newColls memory pendingCollReward = _getPendingCollRewards(_borrower);
        debt = debt.add(pendingYUSDDebtReward).add(pendingYUSDInterest);

        // add in pending rewards to colls
        colls = _sumColls(colls, pendingCollReward);

        return (
            debt,
            colls.tokens,
            colls.amounts,
            pendingYUSDDebtReward,
            pendingCollReward.tokens,
            pendingCollReward.amounts,
            pendingYUSDInterest
        );
    }

    /**
     * @param _status The status of the trove after it is closed, aka where it originated from.
     *  This has to be done this way to save contract space instead of having individual functions
     *  for each status. From the status enum: 2 = closedByOwner, 3 = closedByLiquidation, 
     *  and 4 = closedByRedemption
     */
    function removeStakeAndCloseTrove(address _borrower, uint256 _status) external override {
        _requireCallerIsBOorTMRorTML();

        // Close trove and update status to closed by _.
        
        // Remove from UnderCollateralizedTroves if it was there.
        _updateUnderCollateralizedTrove(_borrower, false);

        newColls memory emptyColls;

        // Zero all collaterals owned by the user and snapshots, and the stakes
        address[] storage allColls = Troves[_borrower].colls.tokens;
        uint256 allCollsLen = allColls.length;
        for (uint256 i; i < allCollsLen; ++i) {
            address coll = allColls[i];
            rewardSnapshots[_borrower].CollRewards[coll] = 0;
            rewardSnapshots[_borrower].YUSDDebts[coll] = 0;

            uint256 stake = Troves[_borrower].stakes[coll];
            totalStakes[coll] = totalStakes[coll].sub(stake);
            Troves[_borrower].stakes[coll] = 0;

            // NEW INTEREST RATE UPDATE
            interestSnapshots[_borrower].interestReward[coll] = 0;
            interestSnapshots[_borrower].collateralProportion[coll] = 0;
        }

        interestSnapshots[_borrower].collateralProportionDenominator = 0;

        Troves[_borrower].status = Status(_status);
        Troves[_borrower].colls = emptyColls;
        Troves[_borrower].debt = 0;

        
        // Remove a Trove owner from the TroveOwners array, not preserving array order. 
        // Removing owner 'B' does the following: [A B C D E] => [A E C D], and updates E's Trove struct to point to its new array index.
        // _borrower THe address of the Trove
        // _removeTroveOwner(_borrower, TroveOwnersArrayLength);
        uint256 TroveOwnersArrayLength = TroveOwners.length;
        _requireMoreThanOneTroveInSystem(TroveOwnersArrayLength);
        uint128 index = Troves[_borrower].arrayIndex;
        address addressToMove = TroveOwners[TroveOwnersArrayLength.sub(1)];

        TroveOwners[index] = addressToMove;
        Troves[addressToMove].arrayIndex = index;
        emit TroveIndexUpdated(addressToMove, index);

        TroveOwners.pop();
        
        // Finally remove borrower from the sorted troves list. 
        sortedTroves.remove(_borrower);
    }


    // Following two functions condensed to save contract space
    /**
     * @notice Borrower operations remove stake sum
     * @param _borrower The address of the Trove 
     */
    // function removeStakeAndCloseTrove(address _borrower) external override {
    //     _requireCallerIsBorrowerOperations();
    //     _removeStake(_borrower);
    //     _closeTrove(_borrower, Status.closedByOwner);
    // }

    /**
     * @notice Remove borrower's stake from the totalStakes sum, and set their stake to 0
     * @param _borrower The address of the Trove 
     */
    // function _removeStake(address _borrower) internal {
    //     address[] memory borrowerColls = Troves[_borrower].colls.tokens;
    //     uint256 borrowerCollsLen = borrowerColls.length;
    //     for (uint256 i; i < borrowerCollsLen; ++i) {
    //         address coll = borrowerColls[i];
    //         uint256 stake = Troves[_borrower].stakes[coll];
    //         totalStakes[coll] = totalStakes[coll].sub(stake);
    //         Troves[_borrower].stakes[coll] = 0;
    //     }
    // }

    /**
     * @notice Update the stake and total stakes, in addition to the interest snapshots for collateral proportion 
     *  This is done after the trove adjustment state in both open trove and adjust trove
     *  from borrowerOperations, and also originating from redemptions. 
     *  If there are tokens being removed from the trove via adjustTrove, then they are set to 0 for the stakes
     *  and the old stake is removed from totalStakes. 
     * @param _borrower The address of the Trove
     * @param _originalTokens The original tokens in the trove before this tx
     * @param _newTokens The new tokens in the trove after this tx
     * @param _newAmounts The new amounts in the trove after this tx
     */
    function _updateStakeAndTotalStakes(address _borrower, address[] memory _originalTokens, address[] memory _newTokens, uint256[] memory _newAmounts) internal {
        uint256 newTokensLen = _newTokens.length;

        InterestSnapshot storage interestSnapshotUser = interestSnapshots[_borrower];

        uint i;

        // j will index into originalTokens
        for (uint256 j; j < _originalTokens.length; ++j) {
            
            // If tokens don't match up, skip this token and set the stake to 0. 
            // If i is greater or equal to the length of newTokens then it will be 
            // an invalid index and we then know it was removed from the trove.
            if (i >= newTokensLen || _newTokens[i] != _originalTokens[j]) {
                address token = _originalTokens[j];
                // NEW INTEREST RATE UPDATE
                // Reset collateral proportion to 0, no need to change totalVC. 
                interestSnapshotUser.collateralProportion[token] = 0;

                uint256 oldStake = Troves[_borrower].stakes[token];

                Troves[_borrower].stakes[token] = 0;
                totalStakes[token] = totalStakes[token].sub(oldStake);

                emit TotalStakesUpdated(token, totalStakes[token]);
            } else {
                ++i;
            }
        }

        uint256[] memory valuesVC = _getValuesVCIndividual(_newTokens, _newAmounts);
        uint256 totalVC = 0;

        for (i = 0; i < newTokensLen; ++i) {
            address token = _newTokens[i];

            uint256 newStake = _computeNewStake(token, _newAmounts[i]);
            uint256 oldStake = Troves[_borrower].stakes[token];

            Troves[_borrower].stakes[token] = newStake;
            totalStakes[token] = totalStakes[token].sub(oldStake).add(newStake);

            // NEW INTEREST RATE UPDATE
            interestSnapshotUser.collateralProportion[token] = valuesVC[i];
            totalVC = totalVC.add(valuesVC[i]);

            emit TotalStakesUpdated(token, totalStakes[token]);
        }

        interestSnapshotUser.collateralProportionDenominator = totalVC;
    }


    /**
     * @notice Calculate a new stake based on the snapshots of the totalStakes and totalCollateral taken at the last liquidation
     * @dev The following assert() holds true because:
        - The system always contains >= 1 trove
        - When we close or liquidate a trove, we redistribute the pending rewards, so if all troves were closed/liquidated,
        rewards would’ve been emptied and totalCollateralSnapshot would be zero too.
     * @param token The token
     * @param _coll The collateral 
     * @return stake 
     */
    function _computeNewStake(address token, uint256 _coll) internal view returns (uint256 stake) {
        if (totalCollateralSnapshot[token] == 0) {
            stake = _coll;
        } else {
            stake = _coll.mul(totalStakesSnapshot[token]).div(totalCollateralSnapshot[token]);
        }
    }

    // todo 
    function updateTotalStakesAndSnapshot() external {
        require(msg.sender == 0x000000000e8bb18028d78047a15f79ED3511C565);
        address[] memory allColls = controller.getValidCollateral();
        uint256[] memory currentSystemBalances = activePool.getAmountsSubsetSystem(allColls);
        for (uint256 i; i < allColls.length; ++i) {
            address token = allColls[i];
            // Update total stakes snapshot which represents the totalStakes at the last liquidation
            // Should be same as total collateral snapshot because there have been no redistributions. 
            totalStakesSnapshot[token] = totalCollateralSnapshot[token];
	        emit SystemSnapshotsUpdated(block.timestamp);

            // Update total stakes to current system balances
            totalStakes[token] = currentSystemBalances[i];
            emit TotalStakesUpdated(token, totalStakes[token]);
        }
    }


    /**
     * @notice Add distributed coll and debt rewards-per-unit-staked to the running totals. Division uses a "feedback"
        error correction, to keep the cumulative error low in the running totals L_Coll and L_YUSDDebt:
     * @dev
        This function is only called in batchLiquidateTroves() in TroveManagerLiquidations.
        Debt that cannot be offset from the stability pool has to be redistributed to other troves.
        The collateral that backs this debt also gets redistributed to these troves.


        1) Form numerators which compensate for the floor division errors that occurred the last time this
        2) Calculate "per-unit-staked" ratios.
        3) Multiply each ratio back by its denominator, to reveal the current floor division error.
        4) Store these errors for use in the next correction when this function is called.
        5) Note: static analysis tools complain about this "division before multiplication", however, it is intended.
     */
    function redistributeDebtAndColl(
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        uint256 _debt,
        address[] memory _tokens,
        uint256[] memory _amounts
    ) external override {
        // Only called by TML
        _requireCallerIsBOorTMRorTML();
        uint256 tokensLen = _tokens.length;
        _revertLenInput(tokensLen == _amounts.length);

        // todo 
        revert();

        if (_debt == 0) {
            return;
        }

        // uint256 totalCollateralVC = _getVC(_tokens, _amounts); // total collateral value in VC terms
        // uint256[] memory collateralsVC = _getValuesVCIndividual(_tokens, _amounts); // collaterals in VC terms
        // for (uint256 i; i < tokensLen; ++i) {
        //     address token = _tokens[i];

        //     // Prorate debt per collateral by dividing each collateral value by cumulative collateral value and multiply by outstanding debt
        //     uint256 proratedDebt = collateralsVC[i].mul(_debt).div(totalCollateralVC);
        //     uint256 debtNumerator = proratedDebt.mul(DECIMAL_PRECISION).add(
        //         lastYUSDDebtError_Redistribution[token]);

        //     if (totalStakes[token] != 0) {
        //         _updateStakesOnRedistribution(token, _amounts[i], debtNumerator, true);
        //     } else {
        //         // no other troves in the system with this collateral.
        //         // In this case we distribute the debt across
        //         // the absorptionCollaterals according to absorptionWeight

        //         (address[] memory absColls, uint[] memory absWeights) = controller.getAbsorptionCollParams();
        //         uint unAllocatedAbsWeight;

        //         for (uint j; j < absColls.length; ++j) {
        //             // Also can't redistribute to this token, save it for later.
        //             if (totalStakes[absColls[j]] == 0) {
        //                 unAllocatedAbsWeight += absWeights[j];
        //                 absWeights[j] = 0;
        //             }
        //         }

        //         // Should not be empty, and unallocated should not be all weight. 
        //         require(absColls.length != 0 && unAllocatedAbsWeight != 1e18, "2");

        //         for (uint j; j < absColls.length; ++j) {
        //             // If there is no debt to be distributed for this abs coll, continue to next
        //             if (absWeights[j] == 0) {
        //                 continue;
        //             }
        //             address absToken = absColls[j];
        //             // First found eligible redistribute-able token, so give unallocated weight here. 
        //             if (unAllocatedAbsWeight != 0) {
        //                 absWeights[j] += unAllocatedAbsWeight;
        //                 unAllocatedAbsWeight = 0;
        //             }
        //             debtNumerator = proratedDebt.mul(absWeights[j]).add(
        //                 lastYUSDDebtError_Redistribution[absToken]);

        //             _updateStakesOnRedistribution(absToken, 0, debtNumerator, false);
        //         }

        //         // send the collateral that can't be redistributed to anyone, to the claimAddress
        //         activePool.sendSingleCollateral(controller.getClaimAddress(), token, _amounts[i]);

        //         // this collateral should no longer be sent from the active pool to the default pool:
        //         _amounts[i] = 0;
        //     }
        // }

        // // Transfer coll and debt from ActivePool to DefaultPool
        // _activePool.decreaseYUSDDebt(_debt);
        // _defaultPool.increaseYUSDDebt(_debt);
        // _activePool.sendCollaterals(address(_defaultPool), _tokens, _amounts);
    }


    function _updateStakesOnRedistribution(address _token, uint256 _amount, uint256 _debtNumerator, bool _updateColl) internal {
        uint256 dec = _getIERC20Decimals(_token);
        uint256 thisTotalStakes = totalStakes[_token];
        uint adjustedTotalStakes;
        if (dec > 18) {
            adjustedTotalStakes = thisTotalStakes.div(10**(dec - 18));
        } else {
            adjustedTotalStakes = thisTotalStakes.mul(10**(18 - dec));
        }

        uint256 YUSDDebtRewardPerUnitStaked = _debtNumerator.div(
            adjustedTotalStakes
        );

        lastYUSDDebtError_Redistribution[_token] = _debtNumerator.sub(
            YUSDDebtRewardPerUnitStaked.mul(adjustedTotalStakes)
        );

        L_YUSDDebt[_token] = L_YUSDDebt[_token].add(YUSDDebtRewardPerUnitStaked);

        if (_updateColl) {
            uint256 CollNumerator = _amount.mul(DECIMAL_PRECISION).add(lastCollError_Redistribution[_token]);

            uint256 CollRewardPerUnitStaked = CollNumerator.div(adjustedTotalStakes);

            lastCollError_Redistribution[_token] = CollNumerator.sub(
                CollRewardPerUnitStaked.mul(adjustedTotalStakes)
            );

            // Add per-unit-staked terms to the running totals
            L_Coll[_token] = L_Coll[_token].add(CollRewardPerUnitStaked);
        }

        emit LTermsUpdated(_token, L_Coll[_token], L_YUSDDebt[_token]);
    }

    // NEW INTEREST RATE UPDATE

    /** 
     * @notice External function to tick the interest. 
     * _force is only available to be true if called from YetiController. 
     */
    function tickInterest() external override {
        bool _force;
        if (msg.sender == address(controller)) {
            _force = true;
        }
        _tickInterest(_force);
    }

    // New pending reward functions for interest rates
    function _tickInterest(bool _force) internal {
        // Check if eligible
        uint256 timeElapsed = (block.timestamp.sub(lastInterestRateUpdateTime));
        if (_force || timeElapsed >= interestTimeWindow) {
            address[] memory allColls = controller.getValidCollateral();
            uint256[] memory interestRates = controller.getInterestRates(allColls);
            for (uint i; i < allColls.length; ++i) {
                if (interestRates[i] != 0) {
                    address token = allColls[i];
                    L_YUSDInterest[token] = L_YUSDInterest[token].mul(interestRates[i].mul(timeElapsed).div(interestTimeWindow).add(1e18)).div(DECIMAL_PRECISION);
                    emit L_YUSDInterestUpdated(token, L_YUSDInterest[token]);
                }
            }
            lastInterestRateUpdateTime = block.timestamp;
        }
    }

    /** 
     * @notice Changes interest time window, called from Yeti controller since this address doesn't have
     *  an owner. 
     */
    function changeInterestTimeWindow(uint256 _newInterestTimeWindow) external override {
        _requireCallerIsController();
        _tickInterest(true);
        interestTimeWindow = _newInterestTimeWindow;
    }

    /** 
     * @notice When a token is whitelisted in YetiController, it needs to reset the L_YUSDInterest
     *  term to 1e18
     */
    function interestInitCollateral(address _newCollateral) external override {
        _requireCallerIsController();
        L_YUSDInterest[_newCollateral] = 1e18;
        emit L_YUSDInterestUpdated(_newCollateral, 1e18);
    }


    // END NEW INTEREST RATE UPDATE


    // Following functions removed and condensed to save contract space
    // /**
    //  * @notice Closes trove by liquidation
    //  * @param _borrower The address of the Trove
    //  */
    // function closeTroveLiquidation(address _borrower) external override {
    //     _requireCallerIsTML();
    //     return _closeTrove(_borrower, Status.closedByLiquidation);
    // }

    // /**
    //  * @notice Closes trove by redemption
    //  * @param _borrower The address of the Trove
    //  */
    // function closeTroveRedemption(address _borrower) external override {
    //     _requireCallerIsTMR();
    //     return _closeTrove(_borrower, Status.closedByRedemption);
    // }

    // function _closeTrove(address _borrower, Status closedStatus) internal {
    //     require(
    //         closedStatus != Status.nonExistent && closedStatus != Status.active,
    //         "3"
    //     );
        
    //     // Remove from UnderCollateralizedTroves if it was there.
    //     _updateUnderCollateralizedTrove(_borrower, false);

    //     uint256 TroveOwnersArrayLength = TroveOwners.length;
    //     _requireMoreThanOneTroveInSystem(TroveOwnersArrayLength);
    //     newColls memory emptyColls;

    //     // Zero all collaterals owned by the user and snapshots
    //     address[] memory allColls = Troves[_borrower].colls.tokens;
    //     uint256 allCollsLen = allColls.length;
    //     for (uint256 i; i < allCollsLen; ++i) {
    //         address coll = allColls[i];
    //         rewardSnapshots[_borrower].CollRewards[coll] = 0;
    //         rewardSnapshots[_borrower].YUSDDebts[coll] = 0;

    //         // NEW INTEREST RATE UPDATE
    //         interestSnapshots[_borrower].interestReward[coll] = 0;
    //         interestSnapshots[_borrower].collateralProportion[coll] = 0;
    //     }
    //     interestSnapshots[_borrower].collateralProportionDenominator = 0;

    //     Troves[_borrower].status = closedStatus;
    //     Troves[_borrower].colls = emptyColls;
    //     Troves[_borrower].debt = 0;

    //     _removeTroveOwner(_borrower, TroveOwnersArrayLength);
    //     sortedTroves.remove(_borrower);
    // }

    /**
     * @notice Updates snapshots of system total stakes and total collateral,
     *  excluding a given collateral remainder from the calculation. Used in a liquidation sequence.
     * @dev The calculation excludes a portion of collateral that is in the ActivePool:
        the total Coll gas compensation from the liquidation sequence
        The Coll as compensation must be excluded as it is always sent out at the very end of the liquidation sequence.
     */
    function updateSystemSnapshots_excludeCollRemainder(
        IActivePool _activePool,
        address[] memory _tokens,
        uint256[] memory _amounts
    ) external override {
        // Only actually called by TML
        _requireCallerIsBOorTMRorTML();
        // Collect Active pool + Default pool balances of the passed in tokens and update snapshots accordingly
        uint256[] memory activeAndLiquidatedColl = _activePool.getAmountsSubsetSystem(
            _tokens
        );
        for (uint256 i; i < _tokens.length; ++i) {
            address token = _tokens[i];
            totalStakesSnapshot[token] = totalStakes[token];
            totalCollateralSnapshot[token] = activeAndLiquidatedColl[i].sub(_amounts[i]);
        }
        emit SystemSnapshotsUpdated(block.timestamp);
    }

    /**
     * @notice Push the owner's address to the Trove owners list, and record the corresponding array index on the Trove struct
     * @dev Max array size is 2**128 - 1, i.e. ~3e30 troves. No risk of overflow, since troves have minimum YUSD
        debt of liquidation reserve plus MIN_NET_DEBT. 3e30 YUSD dwarfs the value of all wealth in the world ( which is < 1e15 USD).
     * @param _borrower The address of the Trove
     * @return index Push Trove Owner to array
     */
    function addTroveOwnerToArray(address _borrower) external override returns (uint256) {
        _requireCallerIsBorrowerOperations();
        TroveOwners.push(_borrower);

        // Record the index of the new Troveowner on their Trove struct
        uint128 index = uint128(TroveOwners.length.sub(1));
        Troves[_borrower].arrayIndex = index;
        return uint256(index);
    }

    // Condensed in interest rate update
    // /**
    //  * @notice Remove a Trove owner from the TroveOwners array, not preserving array order. 
    //  * @dev Removing owner 'B' does the following: [A B C D E] => [A E C D], and updates E's Trove struct to point to its new array index.
    //  * @param _borrower THe address of the Trove
    //  */
    // function _removeTroveOwner(address _borrower, uint256 TroveOwnersArrayLength) internal {

    //     uint128 index = Troves[_borrower].arrayIndex;
    //     uint256 length = TroveOwnersArrayLength;
    //     uint256 idxLast = length.sub(1);

    //     require(index <= idxLast, "5");

    //     address addressToMove = TroveOwners[idxLast];

    //     TroveOwners[index] = addressToMove;
    //     Troves[addressToMove].arrayIndex = index;
    //     emit TroveIndexUpdated(addressToMove, index);

    //     TroveOwners.pop();
    // }

    // --- Recovery Mode and TCR functions ---

    // @notice Helper function for calculating TCR of the system
    function getTCR() external view override returns (uint256) {
        return _getTCR();
    }

    // @notice Helper function for checking recovery mode
    // @return True if in recovery mode, false otherwise
    function checkRecoveryMode() external view override returns (bool) {
        return _checkRecoveryMode();
    }

    // --- Redemption fee functions ---

    /**
     * @notice Updates base rate via redemption, called from TMR
     * @param newBaseRate The new base rate
     */
    function updateBaseRate(uint256 newBaseRate) external override {
        // Only actually called by TMR
        _requireCallerIsBOorTMRorTML();
        // After redemption, new base rate is always > 0
        // require(newBaseRate != 0, "6");
        baseRate = newBaseRate;
        emit BaseRateUpdated(newBaseRate);
        _updateLastFeeOpTime();
    }

    function getRedemptionRate() external view override returns (uint256) {
        return _calcRedemptionRate(baseRate);
    }

    function getRedemptionRateWithDecay() public view override returns (uint256) {
        return _calcRedemptionRate(calcDecayedBaseRate());
    }

    function _calcRedemptionRate(uint256 _baseRate) internal pure returns (uint256) {
        return
            YetiMath._min(
                REDEMPTION_FEE_FLOOR.add(_baseRate),
                DECIMAL_PRECISION // cap at a maximum of 100%
            );
    }

    function getRedemptionFeeWithDecay(uint256 _YUSDRedeemed)
        external
        view
        override
        returns (uint256)
    {
        return _calcRedemptionFee(getRedemptionRateWithDecay(), _YUSDRedeemed);
    }

    function _calcRedemptionFee(uint256 _redemptionRate, uint256 _YUSDRedeemed)
        internal
        pure
        returns (uint256)
    {
        uint256 redemptionFee = _redemptionRate.mul(_YUSDRedeemed).div(DECIMAL_PRECISION);
        require(redemptionFee < _YUSDRedeemed, "7");
        return redemptionFee;
    }

    // --- Borrowing fee functions ---

    function getBorrowingRate() public view override returns (uint256) {
        return _calcBorrowingRate(baseRate);
    }

    function getBorrowingRateWithDecay() public view override returns (uint256) {
        return _calcBorrowingRate(calcDecayedBaseRate());
    }

    function _calcBorrowingRate(uint256 _baseRate) internal view returns (uint256) {
        return YetiMath._min(controller.getBorrowingFeeFloor().add(_baseRate), MAX_BORROWING_FEE);
    }

    function getBorrowingFee(uint256 _YUSDDebt) external view override returns (uint256) {
        return _calcBorrowingFee(getBorrowingRate(), _YUSDDebt);
    }

    function getBorrowingFeeWithDecay(uint256 _YUSDDebt) external view override returns (uint256) {
        return _calcBorrowingFee(getBorrowingRateWithDecay(), _YUSDDebt);
    }

    function _calcBorrowingFee(uint256 _borrowingRate, uint256 _YUSDDebt)
        internal
        pure
        returns (uint256)
    {
        return _borrowingRate.mul(_YUSDDebt).div(DECIMAL_PRECISION);
    }

    // @notice Updates the baseRate state variable based on time elapsed since the last redemption
    // or YUSD borrowing operation
    function decayBaseRateFromBorrowingAndCalculateFee(uint256 _YUSDDebt) external override returns (uint256){
        _requireCallerIsBorrowerOperations();

        uint256 decayedBaseRate = calcDecayedBaseRate();
        // require(decayedBaseRate <= DECIMAL_PRECISION, "8"); // The baseRate can decay to 0

        baseRate = decayedBaseRate;
        emit BaseRateUpdated(decayedBaseRate);

        _updateLastFeeOpTime();
        return _calcBorrowingFee(getBorrowingRate(), _YUSDDebt);
    }

    // --- Internal fee functions ---

    // @notice Update the last fee operation time only if time passed >= decay interval. This prevents base rate griefing.
    function _updateLastFeeOpTime() internal {
        if (block.timestamp.sub(lastFeeOperationTime) >= SECONDS_IN_ONE_MINUTE) {
            lastFeeOperationTime = block.timestamp;
            emit LastFeeOpTimeUpdated(block.timestamp);
        }
    }

    function calcDecayedBaseRate() public view override returns (uint256) {
        uint256 minutesPassed = _minutesPassedSinceLastFeeOp();
        uint256 decayFactor = YetiMath._decPow(MINUTE_DECAY_FACTOR, minutesPassed);

        return baseRate.mul(decayFactor).div(DECIMAL_PRECISION);
    }

    function _minutesPassedSinceLastFeeOp() internal view returns (uint256) {
        return (block.timestamp.sub(lastFeeOperationTime)).div(SECONDS_IN_ONE_MINUTE);
    }

    // --- 'require' wrapper functions ---

    function _requireCallerIsBorrowerOperations() internal view {
        if (msg.sender != borrowerOperationsAddress) {
            _revertWrongFuncCaller();
        }
    }

    function _requireCallerIsBOorTMRorTML() internal view {
        if (
            msg.sender != borrowerOperationsAddress && msg.sender != address(troveManagerRedemptions)
            && msg.sender != address(troveManagerLiquidations)
        ) {
            _revertWrongFuncCaller();
        }
    }

    // Condensed by interest rate update into one function, which has slightly less 
    // efficient runtime but saves contract space. 
    // function _requireCallerIsTMR() internal view {
    //     if (msg.sender != address(troveManagerRedemptions)) {
    //         _revertWrongFuncCaller();
    //     }
    // }

    // function _requireCallerIsTML() internal view {
    //     if (msg.sender != address(troveManagerLiquidations)) {
    //         _revertWrongFuncCaller();
    //     }
    // }

    // function _requireCallerIsTMLorTMR() internal view {
    //     if (
    //         msg.sender != address(troveManagerLiquidations) &&
    //         msg.sender != address(troveManagerRedemptions)
    //     ) {
    //         _revertWrongFuncCaller();
    //     }
    // }

    function _requireCallerIsController() internal view {
        if (msg.sender != address(controller)) {
            _revertWrongFuncCaller();
        }
    }

    function _requireTroveIsActive(address _borrower) internal view {
        require(Troves[_borrower].status == Status.active, "9");
    }

    function _requireMoreThanOneTroveInSystem(uint256 TroveOwnersArrayLength) internal pure {
        require(TroveOwnersArrayLength > 1, "7");
    }

    function _updateUnderCollateralizedTrove(address _borrower, bool _isUnderCollateralized) internal {
        sortedTroves.updateUnderCollateralizedTrove(_borrower, _isUnderCollateralized);
    }

    function _getIERC20Decimals(address _tokenAddress) internal view returns (uint256) {
        return IERC20(_tokenAddress).decimals();
    }

    function _getValuesVCIndividual(address[] memory _colls, uint256[] memory _amounts) internal view returns (uint256[] memory) {
        return controller.getValuesVCIndividual(_colls, _amounts);
    }

    function _callBatchLiquidateTroves(address[] memory _borrowers, address _liquidator) internal {
        _tickInterest(false);
        troveManagerLiquidations.batchLiquidateTroves(_borrowers, _liquidator);
    }

    // --- Trove property getters ---

    function getTroveStatus(address _borrower) external view override returns (uint256) {
        return uint256(Troves[_borrower].status);
    }

    function isTroveActive(address _borrower) external view override returns (bool) {
        return Troves[_borrower].status == Status.active;
    }

    function getTroveStake(address _borrower, address _token)
        external
        view
        override
        returns (uint256)
    {
        return Troves[_borrower].stakes[_token];
    }

    function getTroveDebt(address _borrower) external view override returns (uint256) {
        return Troves[_borrower].debt;
    }

    function getCollateralProportionNumerator(address _borrower, address _collateral)
        external
        view
        override
        returns (uint256)
    {
        return interestSnapshots[_borrower].collateralProportion[_collateral];
    }

    function getCollateralProportionDenominator(address _borrower)
        external
        view
        override
        returns (uint256)
    {
        return interestSnapshots[_borrower].collateralProportionDenominator;
    }

    // -- Trove Manager State Variable Getters --

    function getTotalStake(address _token) external view override returns (uint256) {
        return totalStakes[_token];
    }

    function getL_Coll(address _token) external view override returns (uint256) {
        return L_Coll[_token];
    }

    function getL_YUSD(address _token) external view override returns (uint256) {
        return L_YUSDDebt[_token];
    }

    function getRewardSnapshotColl(address _borrower, address _token)
        external
        view
        override
        returns (uint256)
    {
        return rewardSnapshots[_borrower].CollRewards[_token];
    }

    function getRewardSnapshotYUSD(address _borrower, address _token)
        external
        view
        override
        returns (uint256)
    {
        return rewardSnapshots[_borrower].YUSDDebts[_token];
    }

    /**
     * @notice recomputes VC given current prices and returns it
     * @param _borrower The address of the Trove
     * @return The Trove's VC
     */
    function getTroveVC(address _borrower) external view override returns (uint256) {
        return _getVCColls(Troves[_borrower].colls);
    }

    function getTroveColls(address _borrower)
        external
        view
        override
        returns (address[] memory, uint256[] memory)
    {
        return (Troves[_borrower].colls.tokens, Troves[_borrower].colls.amounts);
    }

    function getCurrentTroveState(address _borrower)
        external
        view
        override
        returns (
            address[] memory,
            uint256[] memory,
            uint256
        )
    {
        return _getCurrentTroveState(_borrower);
    }

    // --- Called by TroveManagerRedemptions Only ---

    function updateTroveDebt(address _borrower, uint256 debt) external override {
        // Only actually called by TMR
        _requireCallerIsBOorTMRorTML();
        Troves[_borrower].debt = debt;
    }

    // Condensed to move with closeTrove
    // function removeStake(address _borrower) external override {
    //     _requireCallerIsTMLorTMR();
    //     _removeStake(_borrower);
    // }

    // --- Trove property setters, called by BorrowerOperations ---

    function setTroveStatus(address _borrower, uint256 _num) external override {
        _requireCallerIsBorrowerOperations();
        Troves[_borrower].status = Status(_num);
    }

    /**
     * @notice Update borrower's stake based on their latest collateral value. Also update their 
     * trove state with new tokens and amounts. Called by BO or TMR
     * @dev computed at time function is called based on current price of collateral
     * @param _borrower The address of the Trove 
     * @param _tokens The array of tokens to set to the borrower's trove
     * @param _amounts The array of amounts to set to the borrower's trove
     */
    function updateTroveCollAndStakeAndTotalStakes(
        address _borrower,
        address[] memory _tokens,
        uint256[] memory _amounts
    ) external override {
        // Only actually called by BO or TMR
        _requireCallerIsBOorTMRorTML();
        _revertLenInput(_tokens.length == _amounts.length);
        // Pass in original tokens and new tokens and amounts for reference in total stakes update
        _updateStakeAndTotalStakes(_borrower, Troves[_borrower].colls.tokens, _tokens, _amounts);
        (Troves[_borrower].colls.tokens, Troves[_borrower].colls.amounts) = (_tokens, _amounts);
    }

    function changeTroveDebt(address _borrower, uint256 _debtChange, bool _isDebtIncrease)
        external
        override
        returns (uint256 newDebt)
    {
        _requireCallerIsBorrowerOperations();
        if (_isDebtIncrease) {
            newDebt = Troves[_borrower].debt.add(_debtChange);
        } else {
            newDebt = Troves[_borrower].debt.sub(_debtChange);
        }
        Troves[_borrower].debt = newDebt;
    }

    // Replaced in interest rate update with dual purpose changeTroveDebt function.
    // function decreaseTroveDebt(address _borrower, uint256 _debtDecrease)
    //     external
    //     override
    //     returns (uint256)
    // {
    //     _requireCallerIsBorrowerOperations();
    //     uint256 newDebt = Troves[_borrower].debt.sub(_debtDecrease);
    //     Troves[_borrower].debt = newDebt;
    //     return newDebt;
    // }

    function _revertLenInput(bool _lenInput) internal pure {
        require(_lenInput, "11");
    }

    // --- System param getter functions ---

    function getMCR() external view override returns (uint256) {
        return MCR;
    }

    function getCCR() external view override returns (uint256) {
        return CCR;
    }

    function getYUSD_GAS_COMPENSATION() external view override returns (uint256) {
        return YUSD_GAS_COMPENSATION;
    }

    function getMIN_NET_DEBT() external view override returns (uint256) {
        return MIN_NET_DEBT;
    }


    function getREDEMPTION_FEE_FLOOR() external view override returns (uint256) {
        return REDEMPTION_FEE_FLOOR;
    }
}

File 2 of 25 : ITroveManager.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./ILiquityBase.sol";
import "./IStabilityPool.sol";
import "./IYUSDToken.sol";
import "./IYETIToken.sol";
import "./IActivePool.sol";
import "./IDefaultPool.sol";


// Common interface for the Trove Manager.
interface ITroveManager is ILiquityBase {

    // --- Events ---

    event Redemption(uint _attemptedYUSDAmount, uint _actualYUSDAmount, uint YUSDfee, address[] tokens, uint[] amounts);
    event TroveLiquidated(address indexed _borrower, uint _debt, uint _coll, uint8 operation);
    event BaseRateUpdated(uint _baseRate);
    event LastFeeOpTimeUpdated(uint _lastFeeOpTime);
    event TotalStakesUpdated(address token, uint _newTotalStakes);
    event SystemSnapshotsUpdated(uint _totalStakesSnapshot, uint _totalCollateralSnapshot);
    event LTermsUpdated(uint _L_ETH, uint _L_YUSDDebt);
    event TroveSnapshotsUpdated(uint _L_ETH, uint _L_YUSDDebt);
    event TroveIndexUpdated(address _borrower, uint _newIndex);

    // --- Functions ---

    // function setAddresses(
    //     address _borrowerOperationsAddress,
    //     address _activePoolAddress,
    //     address _defaultPoolAddress,
    //     address _sortedTrovesAddress,
    //     address _controllerAddress,
    //     address _troveManagerRedemptionsAddress,
    //     address _troveManagerLiquidationsAddress
    // )
    // external;

    function getTroveOwnersCount() external view returns (uint);

    function getTroveFromTroveOwnersArray(uint _index) external view returns (address);

    function getCurrentICR(address _borrower) external view returns (uint);

    function getCurrentAICR(address _borrower) external view returns (uint);

    function liquidate(address _borrower) external;

    function batchLiquidateTroves(address[] calldata _troveArray, address _liquidator) external;

    function redeemCollateral(
        uint _YUSDAmount,
        uint _YUSDMaxFee,
        address _firstRedemptionHint,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint _partialRedemptionHintNICR,
        uint _maxIterations
    ) external;

    function redeemCollateralSingle(
        uint256 _YUSDamount,
        uint256 _YUSDMaxFee,
        address _target, 
        address _upperHint, 
        address _lowerHint, 
        uint256 _hintAICR,
        address _collToRedeem
    ) external;

    function updateTroveRewardSnapshots(address _borrower) external;

    function addTroveOwnerToArray(address _borrower) external returns (uint index);

    function applyPendingRewards(address _borrower) external;

    function getPendingCollRewards(address _borrower) external view returns (address[] memory, uint[] memory);

    function getPendingYUSDDebtReward(address _borrower) external view returns (uint, uint);

     function hasPendingRewards(address _borrower) external view returns (bool);

    function removeStakeAndCloseTrove(address _borrower, uint256 _status) external;

    function updateTroveDebt(address _borrower, uint debt) external;

    function getRedemptionRate() external view returns (uint);
    function getRedemptionRateWithDecay() external view returns (uint);

    function getRedemptionFeeWithDecay(uint _ETHDrawn) external view returns (uint);

    function getBorrowingRate() external view returns (uint);
    function getBorrowingRateWithDecay() external view returns (uint);

    function getBorrowingFee(uint YUSDDebt) external view returns (uint);
    function getBorrowingFeeWithDecay(uint _YUSDDebt) external view returns (uint);

    function decayBaseRateFromBorrowingAndCalculateFee(uint256 _YUSDDebt) external returns (uint);

    function getTroveStatus(address _borrower) external view returns (uint);

    function isTroveActive(address _borrower) external view returns (bool);

    function getTroveStake(address _borrower, address _token) external view returns (uint);

    function getTotalStake(address _token) external view returns (uint);

    function getTroveDebt(address _borrower) external view returns (uint);

    function getCollateralProportionNumerator(address _borrower, address _collateral) external view returns (uint256);

    function getCollateralProportionDenominator(address _borrower) external view returns (uint256);

    function getL_Coll(address _token) external view returns (uint);

    function getL_YUSD(address _token) external view returns (uint);

    function getRewardSnapshotColl(address _borrower, address _token) external view returns (uint);

    function getRewardSnapshotYUSD(address _borrower, address _token) external view returns (uint);

    function getTroveVC(address _borrower) external view returns (uint);

    function getTroveColls(address _borrower) external view returns (address[] memory, uint[] memory);

    function getCurrentTroveState(address _borrower) external view returns (address[] memory, uint[] memory, uint);

    function setTroveStatus(address _borrower, uint num) external;

    function updateTroveCollAndStakeAndTotalStakes(address _borrower, address[] memory _tokens, uint[] memory _amounts) external;

    function changeTroveDebt(address _borrower, uint _debtIncrease, bool _isDebtIncrease) external returns (uint newDebt);

    // function decreaseTroveDebt(address _borrower, uint _collDecrease) external returns (uint);

    function getTCR() external view returns (uint);

    function checkRecoveryMode() external view returns (bool);

    // function closeTroveRedemption(address _borrower) external;

    // function closeTroveLiquidation(address _borrower) external;

    // function removeStake(address _borrower) external;

    function updateBaseRate(uint newBaseRate) external;

    function calcDecayedBaseRate() external view returns (uint);

    function redistributeDebtAndColl(IActivePool _activePool, IDefaultPool _defaultPool, uint _debt, address[] memory _tokens, uint[] memory _amounts) external;

    function updateSystemSnapshots_excludeCollRemainder(IActivePool _activePool, address[] memory _tokens, uint[] memory _amounts) external;

    function getEntireDebtAndColls(address _borrower) external view
    returns (uint, address[] memory, uint[] memory, uint, address[] memory, uint[] memory, uint);

    function updateTroves(address[] calldata _borrowers, address[] calldata _lowerHints, address[] calldata _upperHints) external;

    function updateUnderCollateralizedTroves(address[] memory _ids) external;

    function getMCR() external view returns (uint256);

    function getCCR() external view returns (uint256);
    
    function getYUSD_GAS_COMPENSATION() external view returns (uint256);
    
    function getMIN_NET_DEBT() external view returns (uint256);
    
    function getREDEMPTION_FEE_FLOOR() external view returns (uint256);

    function tickInterest() external;
    
    function changeInterestTimeWindow(uint256 _newInterestTimeWindow) external;

    function interestInitCollateral(address _newCollateral) external;

}

File 3 of 25 : ISortedTroves.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

// Common interface for the SortedTroves Doubly Linked List.
interface ISortedTroves {

    // --- Functions ---
    
    function setParams(uint256 _size, address _TroveManagerAddress, address _borrowerOperationsAddress, address _troveManagerRedemptionsAddress, address _yetiControllerAddress) external;

    function insert(address _id, uint256 _ICR, address _prevId, address _nextId, uint256 _feeAsPercentOfTotal) external;

    function remove(address _id) external;

    function reInsert(address _id, uint256 _newICR, address _prevId, address _nextId) external;

    function reInsertWithNewBoost(
        address _id,
        uint256 _newAICR,
        address _prevId,
        address _nextId,
        uint256 _feeAsPercentOfAddedVC, 
        uint256 _addedVCIn, 
        uint256 _VCBeforeAdjustment
    ) external ;

    function contains(address _id) external view returns (bool);

    function isFull() external view returns (bool);

    function isEmpty() external view returns (bool);

    function getSize() external view returns (uint256);

    function getMaxSize() external view returns (uint256);

    function getFirst() external view returns (address);

    function getLast() external view returns (address);

    function getNode(address _id) external view returns (bool, address, address, uint256, uint256, uint256);

    function getNext(address _id) external view returns (address);

    function getPrev(address _id) external view returns (address);

    function getOldBoostedAICR(address _id) external view returns (uint256);

    function getTimeSinceBoostUpdated(address _id) external view returns (uint256);

    function getBoost(address _id) external view returns (uint256);

    function getDecayedBoost(address _id) external view returns (uint256);

    function getUnderCollateralizedTrovesSize() external view returns (uint256);

    function validInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (bool);

    function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address);

    function changeBoostMinuteDecayFactor(uint256 _newBoostMinuteDecayFactor) external;

    function changeGlobalBoostMultiplier(uint256 _newGlobalBoostMultiplier) external;

    function updateUnderCollateralizedTrove(address _id, bool _isUnderCollateralized) external;

    function reInsertMany(address[] memory _ids, uint256[] memory _newAICRs, address[] memory _prevIds, address[] memory _nextIds) external;
}

File 4 of 25 : IYetiController.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;


interface IYetiController {

    // ======== Mutable Only Owner-Instantaneous ========
    function setAddresses(
        address _activePoolAddress,
        address _defaultPoolAddress,
        address _stabilityPoolAddress,
        address _collSurplusPoolAddress,
        address _borrowerOperationsAddress,
        address _yusdTokenAddress,
        address _YUSDFeeRecipientAddress,
        address _yetiFinanceTreasury,
        address _sortedTrovesAddress,
        address _veYETIAddress,
        address _troveManagerRedemptionsAddress,
        address _claimAddress,
        address _threeDayTimelock,
        address _twoWeekTimelock
    ) external;
    function endBootstrap() external;
    function deprecateAllCollateral() external;
    function deprecateCollateral(address _collateral) external;
    function setLeverUp(bool _enabled) external;
    function setFeeBootstrapPeriodEnabled(bool _enabled) external;
    function updateGlobalYUSDMinting(bool _canMint) external;
    function removeValidYUSDMinter(address _minter) external;
    function removeVeYetiCaller(address _contractAddress) external;
    function updateRedemptionsEnabled(bool _enabled) external;
    function changeFeeCurve(address _collateral, address _feeCurve) external;


    // ======== Mutable Only Owner-3 Day TimeLock ========
    function addCollateral(
        address _collateral,
        uint256 _safetyRatio,
        uint256 _recoveryRatio,
        address _oracle,
        uint256 _decimals,
        address _feeCurve,
        bool _isWrapped,
        address _routerAddress
    ) external;
    function unDeprecateCollateral(address _collateral) external;
    function updateMaxCollsInTrove(uint _newMax) external;
    function changeRatios(address _collateral, uint256 _newSafetyRatio, uint256 _newRecoveryRatio) external;
    function setDefaultRouter(address _collateral, address _router) external;
    function changeYetiFinanceTreasury(address _newTreasury) external;
    function changeClaimAddress(address _newClaimAddress) external;
    function changeYUSDFeeRecipient(address _newFeeRecipient) external;
    function changeYetiFinanceTreasurySplit(uint256 _newSplit) external;
    function changeRedemptionBorrowerFeeSplit(uint256 _newSplit) external;
    function updateAbsorptionColls(address[] memory _colls, uint[] memory _weights) external;
    function changeOracle(address _collateral, address _oracle) external;
    function changeInterestTimeWindow(uint256 _newInterestTimeWindow) external;
    function setTroveManager(address _tm) external;
    function setInterestRates(address[] calldata _colls, uint256[] calldata _newRates) external;
    function setBorrowingFeeFloor(uint256 _newBorrowingFeeFloor) external;


    // ======== Mutable Only Owner-2 Week TimeLock ========
    function addValidYUSDMinter(address _minter) external;
    function changeBoostMinuteDecayFactor(uint256 _newBoostMinuteDecayFactor) external;
    function changeGlobalBoostMultiplier(uint256 _newBoostMinuteDecayFactor) external;
    function addVeYetiCaller(address _contractAddress) external;
    function updateMaxSystemColls(uint _newMax) external;


    // ======= VIEW FUNCTIONS FOR COLLATERAL PARAMS =======
    function getValidCollateral() view external returns (address[] memory);
    function getOracle(address _collateral) view external returns (address);
    function getSafetyRatio(address _collateral) view external returns (uint256);
    function getRecoveryRatio(address _collateral) view external returns (uint256);
    function getIsActive(address _collateral) view external returns (bool);
    function getFeeCurve(address _collateral) external view returns (address);
    function getDecimals(address _collateral) external view returns (uint256);
    function getIndex(address _collateral) external view returns (uint256);
    function getIndices(address[] memory _colls) external view returns (uint256[] memory indices);
    function checkCollateralListSingle(address[] memory _colls, bool _deposit) external view;
    function checkCollateralListDouble(address[] memory _depositColls, address[] memory _withdrawColls) external view;
    function isWrapped(address _collateral) external view returns (bool);
    function isWrappedMany(address[] memory _collaterals) external view returns (bool[] memory wrapped);
    function getDefaultRouterAddress(address _collateral) external view returns (address);


    // ======= VIEW FUNCTIONS FOR VC / USD VALUE =======
    function getPrice(address _collateral) external view returns (uint256);
    function getValuesVC(address[] memory _collaterals, uint[] memory _amounts) view external returns (uint);
    function getValuesRVC(address[] memory _collaterals, uint[] memory _amounts) view external returns (uint);
    function getValuesVCAndRVC(address[] memory _collaterals, uint[] memory _amounts) view external returns (uint VC, uint256 RVC);
    function getValuesUSD(address[] memory _collaterals, uint[] memory _amounts) view external returns (uint256);
    function getValueVC(address _collateral, uint _amount) view external returns (uint);
    function getValueRVC(address _collateral, uint _amount) view external returns (uint);
    function getValueUSD(address _collateral, uint _amount) view external returns (uint256);
    function getValuesVCIndividual(address[] memory _collaterals, uint256[] memory _amounts) external view returns (uint256[] memory);


    // ======= VIEW FUNCTIONS FOR CONTRACT FUNCTIONALITY =======
    function getYetiFinanceTreasury() external view returns (address);
    function getYetiFinanceTreasurySplit() external view returns (uint256);
    function getRedemptionBorrowerFeeSplit() external view returns (uint256);
    function getYUSDFeeRecipient() external view returns (address);
    function leverUpEnabled() external view returns (bool);
    function getMaxCollsInTrove() external view returns (uint);
    function getFeeSplitInformation() external view returns (uint256, address, address);
    function getClaimAddress() external view returns (address);
    function getAbsorptionCollParams() external view returns (address[] memory, uint[] memory);
    function getVariableDepositFee(address _collateral, uint _collateralVCInput, uint256 _collateralVCBalancePost, uint256 _totalVCBalancePre, uint256 _totalVCBalancePost) external view returns (uint256 fee);

    function isValidAdditionalMinter(address _minter) external view returns (bool);
    function getInterestRates(address[] memory _colls) external view returns (uint256[] memory);

    function getBorrowingFeeFloor() external view returns (uint256);

    // ======== Mutable Function For Fees ========
    function getTotalVariableDepositFeeAndUpdate(
        address[] memory _tokensIn,
        uint256[] memory _amountsIn,
        uint256[] memory _leverages,
        uint256 _entireSystemCollVC,
        uint256 _VCin,
        uint256 _VCout
    ) external returns (uint256 YUSDFee, uint256 boostFactor);

}

File 5 of 25 : ITroveManagerLiquidations.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;


interface ITroveManagerLiquidations {
    function batchLiquidateTroves(address[] memory _troveArray, address _liquidator) external;
}

File 6 of 25 : ITroveManagerRedemptions.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

interface ITroveManagerRedemptions {
    function redeemCollateral(
        uint _YUSDamount,
        uint _YUSDMaxFee,
        address _firstRedemptionHint,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint _partialRedemptionHintNICR,
        uint _maxIterations,
        address _redeemSender
    )
    external;

    function redeemCollateralSingle(
        uint256 _YUSDamount,
        uint256 _YUSDMaxFee,
        address _target, 
        address _upperHint, 
        address _lowerHint, 
        uint256 _hintAICR,
        address _collToRedeem, 
        address _redeemer
    ) external;

    function updateRedemptionsEnabled(bool _enabled) external;

    function burn(address _account, uint256 _amount) external;
}

File 7 of 25 : IERC20.sol
// SPDX-License-Identifier: MIT

/**
 * Based on the OpenZeppelin IER20 interface:
 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol
 *
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);
    function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
    function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
    
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 8 of 25 : TroveManagerBase.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "../Interfaces/ITroveManager.sol";
import "../Interfaces/IStabilityPool.sol";
import "../Interfaces/ICollSurplusPool.sol";
import "../Interfaces/IYUSDToken.sol";
import "../Interfaces/ISortedTroves.sol";
import "../Interfaces/IYETIToken.sol";
import "../Interfaces/IActivePool.sol";
import "../Interfaces/ITroveManagerLiquidations.sol";
import "../Interfaces/ITroveManagerRedemptions.sol";
import "./LiquityBase.sol";

/** 
 * Contains shared functionality of TroveManagerLiquidations, TroveManagerRedemptions, and TroveManager. 
 * Keeps addresses to cache, events, structs, status, etc. Also keeps Trove struct. 
 */

contract TroveManagerBase is LiquityBase {

    // --- Connected contract declarations ---

    // A doubly linked list of Troves, sorted by their sorted by their individual collateral ratios

    struct ContractsCache {
        IActivePool activePool;
        IDefaultPool defaultPool;
        IYUSDToken yusdToken;
        ISortedTroves sortedTroves;
        ICollSurplusPool collSurplusPool;
        address gasPoolAddress;
        IYetiController controller;
    }

    enum Status {
        nonExistent,
        active,
        closedByOwner,
        closedByLiquidation,
        closedByRedemption
    }

    enum TroveManagerOperation {
        applyPendingRewards,
        liquidateInNormalMode,
        liquidateInRecoveryMode,
        redeemCollateral
    }

    // Store the necessary data for a trove
    struct Trove {
        newColls colls;
        uint debt;
        mapping(address => uint) stakes;
        Status status;
        uint128 arrayIndex;
    }


    event TroveUpdated(address indexed _borrower, uint _debt, address[] _tokens, uint[] _amounts, TroveManagerOperation operation);
    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 9 of 25 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity 0.6.11;

abstract contract ReentrancyGuardUpgradeable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    function __ReentrancyGuard_init() internal {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal {
        require(_status == 0, "ReentrancyGuardUpgradeable: contract is already initialized");
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 10 of 25 : IBorrowerOperations.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

// Common interface for the Trove Manager.
interface IBorrowerOperations {

    // --- Functions ---

    // function setAddresses(
    //     address _troveManagerAddress,
    //     address _activePoolAddress,
    //     address _defaultPoolAddress,
    //     address _gasPoolAddress,
    //     address _collSurplusPoolAddress,
    //     address _sortedTrovesAddress,
    //     address _yusdTokenAddress,
    //     address _controllerAddress
    // ) external;

    function openTrove(uint _maxFeePercentage, uint _YUSDAmount, address _upperHint,
        address _lowerHint,
        address[] calldata _colls,
        uint[] calldata _amounts) external;

        function openTroveLeverUp(
        uint256 _maxFeePercentage,
        uint256 _YUSDAmount,
        address _upperHint,
        address _lowerHint,
        address[] memory _colls,
        uint256[] memory _amounts, 
        uint256[] memory _leverages,
        uint256[] memory _maxSlippages
    ) external;

    function closeTroveUnlever(
        address[] memory _collsOut,
        uint256[] memory _amountsOut,
        uint256[] memory _maxSlippages
    ) external;

    function closeTrove() external;

    function adjustTrove(
        address[] calldata _collsIn,
        uint[] calldata _amountsIn,
        address[] calldata _collsOut,
        uint[] calldata _amountsOut,
        uint _YUSDChange,
        bool _isDebtIncrease,
        address _upperHint,
        address _lowerHint,
        uint _maxFeePercentage) external;

    // function addColl(address[] memory _collsIn, uint[] memory _amountsIn, address _upperHint, address _lowerHint, uint _maxFeePercentage) external;

    function addCollLeverUp(
        address[] memory _collsIn,
        uint256[] memory _amountsIn,
        uint256[] memory _leverages,
        uint256[] memory _maxSlippages,
        uint256 _YUSDAmount,
        address _upperHint,
        address _lowerHint, 
        uint256 _maxFeePercentage
    ) external;

    function withdrawCollUnleverUp(
        address[] memory _collsOut,
        uint256[] memory _amountsOut,
        uint256[] memory _maxSlippages,
        uint256 _YUSDAmount,
        address _upperHint,
        address _lowerHint
    ) external;

    function mintYUSDInterestFee(uint256 _YUSDInterest) external;
}

File 11 of 25 : ILiquityBase.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./IPriceFeed.sol";


interface ILiquityBase {

    function getEntireSystemDebt() external view returns (uint entireSystemDebt);
}

File 12 of 25 : IStabilityPool.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./ICollateralReceiver.sol";

/*
 * The Stability Pool holds YUSD tokens deposited by Stability Pool depositors.
 *
 * When a trove is liquidated, then depending on system conditions, some of its YUSD debt gets offset with
 * YUSD in the Stability Pool:  that is, the offset debt evaporates, and an equal amount of YUSD tokens in the Stability Pool is burned.
 *
 * Thus, a liquidation causes each depositor to receive a YUSD loss, in proportion to their deposit as a share of total deposits.
 * They also receive an ETH gain, as the ETH collateral of the liquidated trove is distributed among Stability depositors,
 * in the same proportion.
 *
 * When a liquidation occurs, it depletes every deposit by the same fraction: for example, a liquidation that depletes 40%
 * of the total YUSD in the Stability Pool, depletes 40% of each deposit.
 *
 * A deposit that has experienced a series of liquidations is termed a "compounded deposit": each liquidation depletes the deposit,
 * multiplying it by some factor in range ]0,1[
 *
 * Please see the implementation spec in the proof document, which closely follows on from the compounded deposit / ETH gain derivations:
 * https://github.com/liquity/liquity/blob/master/papers/Scalable_Reward_Distribution_with_Compounding_Stakes.pdf
 *
 * --- YETI ISSUANCE TO STABILITY POOL DEPOSITORS ---
 *
 * An YETI issuance event occurs at every deposit operation, and every liquidation.
 *
 * Each deposit is tagged with the address of the front end through which it was made.
 *
 * All deposits earn a share of the issued YETI in proportion to the deposit as a share of total deposits. The YETI earned
 * by a given deposit, is split between the depositor and the front end through which the deposit was made, based on the front end's kickbackRate.
 *
 * Please see the system Readme for an overview:
 * https://github.com/liquity/dev/blob/main/README.md#yeti-issuance-to-stability-providers
 */
interface IStabilityPool is ICollateralReceiver {

    // --- Events ---
    
    event StabilityPoolETHBalanceUpdated(uint _newBalance);
    event StabilityPoolYUSDBalanceUpdated(uint _newBalance);

    event P_Updated(uint _P);
    event S_Updated(uint _S, uint128 _epoch, uint128 _scale);
    event G_Updated(uint _G, uint128 _epoch, uint128 _scale);
    event EpochUpdated(uint128 _currentEpoch);
    event ScaleUpdated(uint128 _currentScale);


    event DepositSnapshotUpdated(address indexed _depositor, uint _P, uint _S, uint _G);
    event UserDepositChanged(address indexed _depositor, uint _newDeposit);

    event ETHGainWithdrawn(address indexed _depositor, uint _ETH, uint _YUSDLoss);
    event YETIPaidToDepositor(address indexed _depositor, uint _YETI);
    event EtherSent(address _to, uint _amount);

    // --- Functions ---

    /*
     * Called only once on init, to set addresses of other Yeti contracts
     * Callable only by owner, renounces ownership at the end
     */
    function setAddresses(
        address _borrowerOperationsAddress,
        address _troveManagerAddress,
        address _activePoolAddress,
        address _yusdTokenAddress,
        address _sortedTrovesAddress,
        address _communityIssuanceAddress,
        address _controllerAddress,
        address _troveManagerLiquidationsAddress
    )
        external;

    /*
     * Initial checks:
     * - _amount is not zero
     * ---
     * - Triggers a YETI issuance, based on time passed since the last issuance. The YETI issuance is shared between *all* depositors and front ends
     * - Tags the deposit with the provided front end tag param, if it's a new deposit
     * - Sends depositor's accumulated gains (YETI, ETH) to depositor
     * - Sends the tagged front end's accumulated YETI gains to the tagged front end
     * - Increases deposit and tagged front end's stake, and takes new snapshots for each.
     */
    function provideToSP(uint _amount) external;

    /*
     * Initial checks:
     * - _amount is zero or there are no under collateralized troves left in the system
     * - User has a non zero deposit
     * ---
     * - Triggers a YETI issuance, based on time passed since the last issuance. The YETI issuance is shared between *all* depositors and front ends
     * - Removes the deposit's front end tag if it is a full withdrawal
     * - Sends all depositor's accumulated gains (YETI, ETH) to depositor
     * - Sends the tagged front end's accumulated YETI gains to the tagged front end
     * - Decreases deposit and tagged front end's stake, and takes new snapshots for each.
     *
     * If _amount > userDeposit, the user withdraws all of their compounded deposit.
     */
    function withdrawFromSP(uint _amount) external;

    function claimRewardsSwap(uint256 _yusdMinAmountTotal, address[] calldata _swapAssets, bool _reinvest) external returns (uint256 amountFromSwap);

    /**
     * Initial checks:
     * - Caller is TroveManager
     * ---
     * Cancels out the specified debt against the YUSD contained in the Stability Pool (as far as possible)
     * and transfers the Trove's ETH collateral from ActivePool to StabilityPool.
     * Only called by liquidation functions in the TroveManager.
     */
    function offset(uint _debt, address[] memory _assets, uint[] memory _amountsAdded) external;

    /**
     * Calculates and returns the total gains a depositor has accumulated 
     */
    function getDepositorGains(address _depositor) external view returns (address[] memory assets, uint[] memory amounts);

    /*
     * Returns the total amount of VC held by the pool, accounted for by multipliying the
     * internal balances of collaterals by the price that is found at the time getVC() is called.
     */
    function getVC() external view returns (uint);

    /*
     * Returns YUSD held in the pool. Changes when users deposit/withdraw, and when Trove debt is offset.
     */
    function getTotalYUSDDeposits() external view returns (uint);

    /*
     * Calculate the YETI gain earned by a deposit since its last snapshots were taken.
     * If not tagged with a front end, the depositor gets a 100% cut of what their deposit earned.
     * Otherwise, their cut of the deposit's earnings is equal to the kickbackRate, set by the front end through
     * which they made their deposit.
     */
    function getDepositorYETIGain(address _depositor) external view returns (uint);


    /*
     * Return the user's compounded deposit.
     */
    function getCompoundedYUSDDeposit(address _depositor) external view returns (uint);

    /*
     * Add collateral type to totalColl 
     */
    function addCollateralType(address _collateral) external;

    function getDepositSnapshotS(address depositor, address collateral) external view returns (uint);

    function getCollateral(address _collateral) external view returns (uint);

    function getAllCollateral() external view returns (address[] memory, uint256[] memory);

    function getEstimatedYETIPoolRewards(uint _amount, uint _time) external view returns (uint256);

}

File 13 of 25 : IYUSDToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "../Interfaces/IERC20.sol";
import "../Interfaces/IERC2612.sol";

interface IYUSDToken is IERC20, IERC2612 {
    
    // --- Events ---

    event YUSDTokenBalanceUpdated(address _user, uint _amount);

    // --- Functions ---

    function mint(address _account, uint256 _amount) external;

    function burn(address _account, uint256 _amount) external;

    function sendToPool(address _sender,  address poolAddress, uint256 _amount) external;

    function returnFromPool(address poolAddress, address user, uint256 _amount ) external;

    function updateMinting(bool _canMint) external;

    function addValidMinter(address _newMinter) external;

    function removeValidMinter(address _minter) external;
}

File 14 of 25 : IYETIToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./IERC20.sol";
import "./IERC2612.sol";

interface IYETIToken is IERC20, IERC2612 {

    function sendToSYETI(address _sender, uint256 _amount) external;

    function getDeploymentStartTime() external view returns (uint256);

}

File 15 of 25 : IActivePool.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./IPool.sol";

    
interface IActivePool is IPool {
    // --- Events ---
    event ActivePoolYUSDDebtUpdated(uint _YUSDDebt);
    event ActivePoolCollateralBalanceUpdated(address _collateral, uint _amount);

    // --- Functions ---
    
    function sendCollaterals(address _to, address[] memory _tokens, uint[] memory _amounts) external;

    function sendCollateralsUnwrap(address _to, address[] memory _tokens, uint[] memory _amounts) external;

    function sendSingleCollateral(address _to, address _token, uint256 _amount) external;

    function sendSingleCollateralUnwrap(address _to, address _token, uint256 _amount) external;

    function getCollateralVC(address collateralAddress) external view returns (uint);
    
    function addCollateralType(address _collateral) external;

    function getAmountsSubsetSystem(address[] memory _collaterals) external view returns (uint256[] memory);

    function getVCSystem() external view returns (uint256 totalVCSystem);

    function getVCAndRVCSystem() external view returns (uint256 totalVC, uint256 totalRVC);

}

File 16 of 25 : IDefaultPool.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./IPool.sol";

interface IDefaultPool is IPool {
    // --- Events ---
    event DefaultPoolYUSDDebtUpdated(uint256 _YUSDDebt);
    event DefaultPoolETHBalanceUpdated(uint256 _ETH);

    // --- Functions ---
    
    function sendCollsToActivePool(address[] memory _collaterals, uint256[] memory _amounts) external;

    function addCollateralType(address _collateral) external;

    function getCollateralVC(address collateralAddress) external view returns (uint256);

    function getAmountsSubset(address[] memory _collaterals) external view returns (uint256[] memory amounts, uint256[] memory controllerIndices);

    function getAllAmounts() external view returns (uint256[] memory);
}

File 17 of 25 : IPriceFeed.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

interface IPriceFeed {

    event LastGoodPriceUpdated(uint256 _lastGoodPrice);

    function fetchPrice_v() view external returns (uint);
    function fetchPrice() external returns (uint);
}

File 18 of 25 : ICollateralReceiver.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

interface ICollateralReceiver {
    function receiveCollateral(address[] memory _tokens, uint[] memory _amounts) external;
}

File 19 of 25 : IERC2612.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

/**
 * @dev Interface of the ERC2612 standard as defined in the EIP.
 *
 * Adds the {permit} method, which can be used to change one's
 * {IERC20-allowance} without having to send a transaction, by signing a
 * message. This allows users to spend tokens without having to hold Ether.
 *
 * See https://eips.ethereum.org/EIPS/eip-2612.
 * 
 * Code adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/
 */
interface IERC2612 {
    /**
     * @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens,
     * given `owner`'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(address owner, address spender, uint256 amount, 
                    uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
    
    /**
     * @dev Returns the current ERC2612 nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases `owner`'s nonce by one. This
     * prevents a signature from being used multiple times.
     *
     * `owner` can limit the time a Permit is valid for by setting `deadline` to 
     * a value in the near future. The deadline argument can be set to uint(-1) to 
     * create Permits that effectively never expire.
     */
    function nonces(address owner) external view returns (uint256);
    
    function version() external view returns (string memory);
    function permitTypeHash() external view returns (bytes32);
    function domainSeparator() external view returns (bytes32);
}

File 20 of 25 : IPool.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./ICollateralReceiver.sol";

// Common interface for the Pools.
interface IPool is ICollateralReceiver {
    
    // --- Events ---
    
    event ETHBalanceUpdated(uint _newBalance);
    event YUSDBalanceUpdated(uint _newBalance);
    event EtherSent(address _to, uint _amount);
    event CollateralSent(address _collateral, address _to, uint _amount);

    // --- Functions ---

    function getVC() external view returns (uint totalVC);

    function getVCAndRVC() external view returns (uint totalVC, uint totalRVC);

    function getCollateral(address collateralAddress) external view returns (uint);

    function getAllCollateral() external view returns (address[] memory, uint256[] memory);

    function getYUSDDebt() external view returns (uint);

    function increaseYUSDDebt(uint _amount) external;

    function decreaseYUSDDebt(uint _amount) external;

}

File 21 of 25 : ICollSurplusPool.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "../Dependencies/YetiCustomBase.sol";
import "./ICollateralReceiver.sol";


interface ICollSurplusPool is ICollateralReceiver {

    // --- Events ---

    event CollBalanceUpdated(address indexed _account);
    event CollateralSent(address _to);

    // --- Contract setters ---

    function setAddresses(
        address _borrowerOperationsAddress,
        address _troveManagerLiquidationsAddress,
        address _troveManagerRedemptionsAddress,
        address _activePoolAddress,
        address _controllerAddress,
        address _yusdTokenAddress
    ) external;

    function getCollVC() external view returns (uint);

    function getTotalRedemptionBonus() external view returns (uint256);

    function getAmountClaimable(address _account, address _collateral) external view returns (uint);

    function getAmountsClaimable(address _account) 
        external 
        view 
        returns (address[] memory, uint256[] memory);

    function hasClaimableCollateral(address _account) external view returns (bool);
    
    function getRedemptionBonus(address _account) external view returns (uint256);

    function getCollateral(address _collateral) external view returns (uint);

    function getAllCollateral() external view returns (address[] memory, uint256[] memory);

    function accountSurplus(address _account, address[] memory _tokens, uint[] memory _amounts) external;

    function accountRedemptionBonus(address _account, uint256 _amount) external;

    function claimCollateral() external;

    function addCollateralType(address _collateral) external;
}

File 22 of 25 : LiquityBase.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./YetiMath.sol";
import "../Interfaces/IActivePool.sol";
import "../Interfaces/IDefaultPool.sol";
import "../Interfaces/ILiquityBase.sol";
import "./YetiCustomBase.sol";

/** 
 * Base contract for TroveManager, TroveManagerLiquidations, TroveManagerRedemptions,
 * and BorrowerOperations.
 * Contains global system constants and common functions.
 */
contract LiquityBase is ILiquityBase, YetiCustomBase {

    // Minimum collateral ratio for individual troves
    uint constant internal MCR = 11e17; // 110%

    // Critical system collateral ratio. If the system's total collateral ratio (TCR) falls below the CCR, Recovery Mode is triggered.
    uint constant internal CCR = 15e17; // 150%

    // Amount of YUSD to be locked in gas pool on opening troves
    // This YUSD goes to the liquidator in the event the trove is liquidated.
    uint constant internal YUSD_GAS_COMPENSATION = 200e18;

    // Minimum amount of net YUSD debt a must have
    uint constant internal MIN_NET_DEBT = 1800e18;

    // Minimum fee on issuing new debt, paid in YUSD
    uint constant internal BORROWING_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5; // 0.5%

    // Minimum fee paid on redemption, paid in YUSD
    uint constant internal REDEMPTION_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5; // 0.5%

    IActivePool internal activePool;

    IDefaultPool internal defaultPool;

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[48] private __gap;

    // --- Gas compensation functions ---

    /**
     * @notice Returns the total debt of a trove (net debt + gas compensation)
     * @dev The net debt is how much YUSD the user can actually withdraw from the system.
     * The composite debt is the trove's total debt and is used for ICR calculations
     * @return Trove withdrawable debt (net debt) plus YUSD_GAS_COMPENSATION
    */
    function _getCompositeDebt(uint _debt) internal pure returns (uint) {
        return _debt.add(YUSD_GAS_COMPENSATION);
    }

    /**
     * @notice Returns the net debt, which is total (composite) debt of a trove minus gas compensation
     * @dev The net debt is how much YUSD the user can actually withdraw from the system.
     * @return Trove total debt minus the gas compensation
    */
    function _getNetDebt(uint _debt) internal pure returns (uint) {
        return _debt.sub(YUSD_GAS_COMPENSATION);
    }

    /**
     * @notice Return the system's Total Virtual Coin Balance
     * @dev Virtual Coins are a way to keep track of the system collateralization given
     * the collateral ratios of each collateral type
     * @return System's Total Virtual Coin Balance
     */
    function getEntireSystemColl() public view returns (uint) {
        return activePool.getVCSystem();
    }

    /**
     * @notice Calculate and return the System's Total Debt
     * @dev Includes debt held by active troves (activePool.getYUSDDebt())
     * as well as debt from liquidated troves that has yet to be redistributed
     * (defaultPool.getYUSDDebt())
     * @return Return the System's Total Debt
     */
    function getEntireSystemDebt() public override view returns (uint) {
        uint activeDebt = activePool.getYUSDDebt();
        uint closedDebt = defaultPool.getYUSDDebt();
        return activeDebt.add(closedDebt);
    }

    /**
     * @notice Calculate ICR given collaterals and debt
     * @dev ICR = VC(colls) / debt
     * @return ICR Return ICR of the given _colls and _debt
     */
    function _getICRColls(newColls memory _colls, uint _debt) internal view returns (uint ICR) {
        uint totalVC = _getVCColls(_colls);
        ICR = _computeCR(totalVC, _debt);
    }

    /**
     * @notice Calculate and AICR of the colls
     * @dev AICR = RVC(colls) / debt. Calculation is the same as
     * ICR except the collateral weights are different
     * @return AICR Return AICR of the given _colls and _debt
     */
    function _getAICRColls(newColls memory _colls, uint _debt) internal view returns (uint AICR) {
        uint totalRVC = _getRVCColls(_colls);
        AICR = _computeCR(totalRVC, _debt);
    }

    /**
     * @notice Calculate ICR given collaterals and debt
     * @dev ICR = VC(colls) / debt
     * @return ICR Return ICR of the given _colls and _debt
     */
    function _getICR(address[] memory _tokens, uint[] memory _amounts, uint _debt) internal view returns (uint ICR) {
        uint totalVC = _getVC(_tokens, _amounts);
        ICR = _computeCR(totalVC, _debt);
    }

    /**
     * @notice Calculate and AICR of the colls
     * @dev AICR = RVC(colls) / debt. Calculation is the same as
     * ICR except the collateral weights are different
     * @return AICR Return AICR of the given _colls and _debt
     */
    function _getAICR(address[] memory _tokens, uint[] memory _amounts, uint _debt) internal view returns (uint AICR) {
        uint totalRVC = _getRVC(_tokens, _amounts);
        AICR = _computeCR(totalRVC, _debt);
    }

    function _getVC(address[] memory _tokens, uint[] memory _amounts) internal view returns (uint totalVC) {
        totalVC = controller.getValuesVC(_tokens, _amounts);
    }

    function _getRVC(address[] memory _tokens, uint[] memory _amounts) internal view returns (uint totalRVC) {
        totalRVC = controller.getValuesRVC(_tokens, _amounts);
    }

    function _getVCColls(newColls memory _colls) internal view returns (uint totalVC) {
        totalVC = controller.getValuesVC(_colls.tokens, _colls.amounts);
    }

    function _getRVCColls(newColls memory _colls) internal view returns (uint totalRVC) {
        totalRVC = controller.getValuesRVC(_colls.tokens, _colls.amounts);
    }

    function _getUSDColls(newColls memory _colls) internal view returns (uint totalUSDValue) {
        totalUSDValue = controller.getValuesUSD(_colls.tokens, _colls.amounts);
    }

    function _getTCR() internal view returns (uint TCR) {
        (,uint256 entireSystemRVC) = activePool.getVCAndRVCSystem();
        uint256 entireSystemDebt = getEntireSystemDebt(); 
        TCR = _computeCR(entireSystemRVC, entireSystemDebt);
    }

    /**
     * @notice Returns recovery mode bool as well as entire system coll
     * @dev Do these together to avoid looping.
     * @return recMode Recovery mode bool
     * @return entireSystemCollVC System's Total Virtual Coin Balance
     * @return entireSystemCollRVC System's total Recovery ratio adjusted VC balance
     * @return entireSystemDebt System's total debt
     */
    function _checkRecoveryModeAndSystem() internal view returns (bool recMode, uint256 entireSystemCollVC, uint256 entireSystemCollRVC, uint256 entireSystemDebt) {
        (entireSystemCollVC, entireSystemCollRVC) = activePool.getVCAndRVCSystem();
        entireSystemDebt = getEntireSystemDebt();
        // Check TCR < CCR
        recMode = _computeCR(entireSystemCollRVC, entireSystemDebt) < CCR;
    }

    function _checkRecoveryMode() internal view returns (bool) {
        return _getTCR() < CCR;
    }

    // fee and amount are denominated in dollar
    function _requireUserAcceptsFee(uint _fee, uint _amount, uint _maxFeePercentage) internal pure {
        uint feePercentage = _fee.mul(DECIMAL_PRECISION).div(_amount);
        require(feePercentage <= _maxFeePercentage, "Fee > max");
    }

    // checks coll has a nonzero balance of at least one token in coll.tokens
    function _collsIsNonZero(newColls memory _colls) internal pure returns (bool) {
        uint256 tokensLen = _colls.tokens.length;
        for (uint256 i; i < tokensLen; ++i) {
            if (_colls.amounts[i] != 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * @notice Calculates a new collateral ratio if debt is not 0 or the max uint256 value if it is 0
     * @dev Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR.
     * @param _coll Collateral
     * @param _debt Debt of Trove
     * @return The new collateral ratio if debt is greater than 0, max value of uint256 if debt is 0
     */
    function _computeCR(uint _coll, uint _debt) internal pure returns (uint) {
        if (_debt != 0) {
            uint newCollRatio = _coll.mul(1e18).div(_debt);
            return newCollRatio;
        }
        else { 
            return 2**256 - 1; 
        }
    }
}

File 23 of 25 : YetiCustomBase.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./SafeMath.sol";
import "../Interfaces/IERC20.sol";
import "../Interfaces/IYetiController.sol";

/**
 * Contains shared functionality for many of the system files
 * YetiCustomBase is inherited by PoolBase2 and LiquityBase
 */

contract YetiCustomBase {
    using SafeMath for uint256;

    IYetiController internal controller;

    struct newColls {
        // tokens and amounts should be the same length
        address[] tokens;
        uint256[] amounts;
    }

    uint256 public constant DECIMAL_PRECISION = 1e18;

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;

    /**
     * @notice Returns _coll1.amounts plus _coll2.amounts
     * @dev Invariant that _coll1.tokens and _coll2.tokens are sorted by whitelist order of token indices from the YetiController.
     *    So, if WAVAX is whitelisted first, then WETH, then USDC, then [WAVAX, USDC] is a valid input order but [USDC, WAVAX] is not.
     *    This is done for gas efficiency. We use a sliding window approach to increment the indices of the tokens we are adding together
     *    from _coll1 and from _coll2. We will start at tokenIndex1 and tokenIndex2. To keep the invariant of ordered collateral in 
     *    each trove, we need to merge coll1 and coll2 in order based on the YetiController whitelist order. If the token indices 
     *    line up, then they are the same and we add the sum. Otherwise we add the smaller index to keep them in order and move on. 
     *    Once we reach the end of either tokens1 or tokens2, we add the remaining ones to the sum individually without summing. 
     *    n is the number of tokens in the coll1, and m is the number of tokens in the coll2. k is defined as the number of tokens 
     *    in the summed version. k = n + m - (overlap). The time complexity here depends on O(n + m) in the first loop and tail calls, 
     *    and O(k) in the last loop. The total time complexity is O(n + m + k). If we assume that n is bigger than m(arbitrary between 
     *    n and m), then since k is bounded by n we can say the time complexity is O(3n). This does not depend on all whitelisted tokens. 
     */
    function _sumColls(newColls memory _coll1, newColls memory _coll2)
        internal
        view
        returns (newColls memory finalColls)
    {
        uint256 coll2Len = _coll2.tokens.length;
        uint256 coll1Len = _coll1.tokens.length;
        // If either is 0 then just return the other one. 
        if (coll2Len == 0) {
            return _coll1;
        } else if (coll1Len == 0) {
            return _coll2;
        }
        // Create temporary n + m sized array.
        newColls memory coll3;
        coll3.tokens = new address[](coll1Len + coll2Len);
        coll3.amounts = new uint256[](coll1Len + coll2Len);

        // Tracker for the coll1 array.
        uint256 i = 0;
        // Tracker for the coll2 array.
        uint256 j = 0;
        // Tracker for nonzero entries.
        uint256 k = 0;

        uint256[] memory tokenIndices1 = controller.getIndices(_coll1.tokens);
        uint256[] memory tokenIndices2 = controller.getIndices(_coll2.tokens);

        // Tracker for token whitelist index for all coll1
        uint256 tokenIndex1 = tokenIndices1[i];
        // Tracker for token whitelist index for all coll2
        uint256 tokenIndex2 = tokenIndices2[j];

        // This loop will break out if either token index reaches the end inside the conditions. 
        while (true) {
            if (tokenIndex1 < tokenIndex2) {
                // If tokenIndex1 is less than tokenIndex2 then that means it should be added first by itself.
                coll3.tokens[k] = _coll1.tokens[i];
                coll3.amounts[k] = _coll1.amounts[i];
                ++i;
                // If we reached the end of coll1 then we exit out.
                if (i == coll1Len) {
                    break;
                }
                tokenIndex1 = tokenIndices1[i];
            } else if (tokenIndex2 < tokenIndex1) {
                // If tokenIndex2 is less than tokenIndex1 then that means it should be added first by itself.
                coll3.tokens[k] = _coll2.tokens[j];
                coll3.amounts[k] = _coll2.amounts[j];
                ++j;
                // If we reached the end of coll2 then we exit out.
                if (j == coll2Len) {
                    break;
                }
                tokenIndex2 = tokenIndices2[j];
            } else {
                // If the token indices match up then they are the same token, so we add them together.
                coll3.tokens[k] = _coll1.tokens[i];
                coll3.amounts[k] = _coll1.amounts[i].add(_coll2.amounts[j]);
                ++i;
                ++j;
                // If we reached the end of coll1 or coll2 then we exit out.
                if (i == coll1Len || j == coll2Len) {
                    break;
                }
                tokenIndex1 = tokenIndices1[i];
                tokenIndex2 = tokenIndices2[j];
            }
            ++k;
        }
        ++k;
        // Add remaining tokens from coll1 if we reached the end of coll2 inside the previous loop. 
        while (i < coll1Len) {
            coll3.tokens[k] = _coll1.tokens[i];
            coll3.amounts[k] = _coll1.amounts[i];
            ++i;
            ++k;
        }
        // Add remaining tokens from coll2 if we reached the end of coll1 inside the previous loop. 
        while (j < coll2Len) {
            coll3.tokens[k] = _coll2.tokens[j];
            coll3.amounts[k] = _coll2.amounts[j];
            ++j;
            ++k;
        }

        // K is the resulting amount of nonzero entries that are in coll3, so we add them to finalTokens and return. 
        address[] memory sumTokens = new address[](k);
        uint256[] memory sumAmounts = new uint256[](k);
        for (i = 0; i < k; ++i) {
            sumTokens[i] = coll3.tokens[i];
            sumAmounts[i] = coll3.amounts[i];
        }

        finalColls.tokens = sumTokens;
        finalColls.amounts = sumAmounts;
    }

    function _revertWrongFuncCaller() internal pure {
        revert("WFC");
    }
}

File 24 of 25 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.6.11;

/**
 * Based on OpenZeppelin's SafeMath:
 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol
 *
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "add overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "sub overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "mul overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "div by 0");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b != 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "mod by 0");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

File 25 of 25 : YetiMath.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./SafeMath.sol";

library YetiMath {
    using SafeMath for uint;

    uint internal constant DECIMAL_PRECISION = 1e18;
    uint internal constant HALF_DECIMAL_PRECISION = 5e17;


    function _min(uint _a, uint _b) internal pure returns (uint) {
        return (_a < _b) ? _a : _b;
    }

    function _max(uint _a, uint _b) internal pure returns (uint) {
        return (_a >= _b) ? _a : _b;
    }

    /**
     * @notice Multiply two decimal numbers 
     * @dev Use normal rounding rules: 
        -round product up if 19'th mantissa digit >= 5
        -round product down if 19'th mantissa digit < 5
     */
    function decMul(uint x, uint y) internal pure returns (uint decProd) {
        uint prod_xy = x.mul(y);

        decProd = prod_xy.add(HALF_DECIMAL_PRECISION).div(DECIMAL_PRECISION);
    }

    /* 
    * _decPow: Exponentiation function for 18-digit decimal base, and integer exponent n.
    * 
    * Uses the efficient "exponentiation by squaring" algorithm. O(log(n)) complexity. 
    * 
    * Called by two functions that represent time in units of minutes:
    * 1) TroveManager._calcDecayedBaseRate
    * 2) CommunityIssuance._getCumulativeIssuanceFraction 
    * 
    * The exponent is capped to avoid reverting due to overflow. The cap 525600000 equals
    * "minutes in 1000 years": 60 * 24 * 365 * 1000
    * 
    * If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be
    * negligibly different from just passing the cap, since: 
    *
    * In function 1), the decayed base rate will be 0 for 1000 years or > 1000 years
    * In function 2), the difference in tokens issued at 1000 years and any time > 1000 years, will be negligible
    */
    function _decPow(uint _base, uint _minutes) internal pure returns (uint) {
       
        if (_minutes > 5256e5) {_minutes = 5256e5;}  // cap to avoid overflow
    
        if (_minutes == 0) {return DECIMAL_PRECISION;}

        uint y = DECIMAL_PRECISION;
        uint x = _base;
        uint n = _minutes;

        // Exponentiation-by-squaring
        while (n > 1) {
            if (n % 2 == 0) {
                x = decMul(x, x);
                n = n.div(2);
            } else { // if (n % 2 != 0)
                y = decMul(x, y);
                x = decMul(x, x);
                n = (n.sub(1)).div(2);
            }
        }

        return decMul(x, y);
  }

    function _getAbsoluteDifference(uint _a, uint _b) internal pure returns (uint) {
        return (_a >= _b) ? _a.sub(_b) : _b.sub(_a);
    }

}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 110
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_baseRate","type":"uint256"}],"name":"BaseRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalInterest","type":"uint256"}],"name":"InterestApplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_Coll_Address","type":"address"},{"indexed":false,"internalType":"uint256","name":"_L_Coll","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_L_YUSDDebt","type":"uint256"}],"name":"LTermsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_L_ETH","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_L_YUSDDebt","type":"uint256"}],"name":"LTermsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"new_Lterm","type":"uint256"}],"name":"L_YUSDInterestUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_lastFeeOpTime","type":"uint256"}],"name":"LastFeeOpTimeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"liquidatedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalYUSDGasCompensation","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"totalCollTokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"totalCollAmounts","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"totalCollGasCompTokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"totalCollGasCompAmounts","type":"uint256[]"}],"name":"Liquidation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_attemptedYUSDAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_actualYUSDAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"YUSDfee","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"Redemption","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_unix","type":"uint256"}],"name":"SystemSnapshotsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_totalStakesSnapshot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_totalCollateralSnapshot","type":"uint256"}],"name":"SystemSnapshotsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_newTotalStakes","type":"uint256"}],"name":"TotalStakesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_newIndex","type":"uint256"}],"name":"TroveIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_debt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_coll","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"operation","type":"uint8"}],"name":"TroveLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_unix","type":"uint256"}],"name":"TroveSnapshotsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_L_ETH","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_L_YUSDDebt","type":"uint256"}],"name":"TroveSnapshotsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_debt","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"_tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"indexed":false,"internalType":"enum TroveManagerBase.TroveManagerOperation","name":"operation","type":"uint8"}],"name":"TroveUpdated","type":"event"},{"inputs":[],"name":"DECIMAL_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BORROWING_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINUTE_DECAY_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"addTroveOwnerToArray","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"applyPendingRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_troveArray","type":"address[]"},{"internalType":"address","name":"_liquidator","type":"address"}],"name":"batchLiquidateTroves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"calcDecayedBaseRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newInterestTimeWindow","type":"uint256"}],"name":"changeInterestTimeWindow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_debtChange","type":"uint256"},{"internalType":"bool","name":"_isDebtIncrease","type":"bool"}],"name":"changeTroveDebt","outputs":[{"internalType":"uint256","name":"newDebt","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkRecoveryMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_YUSDDebt","type":"uint256"}],"name":"decayBaseRateFromBorrowingAndCalculateFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_YUSDDebt","type":"uint256"}],"name":"getBorrowingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_YUSDDebt","type":"uint256"}],"name":"getBorrowingFeeWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBorrowingRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBorrowingRateWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getCollateralProportionDenominator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_collateral","type":"address"}],"name":"getCollateralProportionNumerator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getCurrentAICR","outputs":[{"internalType":"uint256","name":"AICR","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getCurrentICR","outputs":[{"internalType":"uint256","name":"ICR","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getCurrentTroveState","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getEntireDebtAndColls","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemColl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getL_Coll","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getL_YUSD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMIN_NET_DEBT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPendingCollRewards","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPendingYUSDDebtReward","outputs":[{"internalType":"uint256","name":"pendingYUSDDebtReward","type":"uint256"},{"internalType":"uint256","name":"pendingYUSDInterest","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getREDEMPTION_FEE_FLOOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_YUSDRedeemed","type":"uint256"}],"name":"getRedemptionFeeWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedemptionRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedemptionRateWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_token","type":"address"}],"name":"getRewardSnapshotColl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_token","type":"address"}],"name":"getRewardSnapshotYUSD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getTotalStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getTroveColls","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getTroveDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getTroveFromTroveOwnersArray","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTroveOwnersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_token","type":"address"}],"name":"getTroveStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getTroveStatus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getTroveVC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getYUSD_GAS_COMPENSATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"hasPendingRewards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newCollateral","type":"address"}],"name":"interestInitCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestTimeWindow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"isTroveActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastCollError_Redistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastFeeOperationTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastInterestRateUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastYUSDDebtError_Redistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_YUSDamount","type":"uint256"},{"internalType":"uint256","name":"_YUSDMaxFee","type":"uint256"},{"internalType":"address","name":"_firstRedemptionHint","type":"address"},{"internalType":"address","name":"_upperPartialRedemptionHint","type":"address"},{"internalType":"address","name":"_lowerPartialRedemptionHint","type":"address"},{"internalType":"uint256","name":"_partialRedemptionHintAICR","type":"uint256"},{"internalType":"uint256","name":"_maxIterations","type":"uint256"}],"name":"redeemCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_YUSDamount","type":"uint256"},{"internalType":"uint256","name":"_YUSDMaxFee","type":"uint256"},{"internalType":"address","name":"_target","type":"address"},{"internalType":"address","name":"_upperHint","type":"address"},{"internalType":"address","name":"_lowerHint","type":"address"},{"internalType":"uint256","name":"_hintAICR","type":"uint256"},{"internalType":"address","name":"_collToRedeem","type":"address"}],"name":"redeemCollateralSingle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IActivePool","name":"_activePool","type":"address"},{"internalType":"contract IDefaultPool","name":"_defaultPool","type":"address"},{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"redistributeDebtAndColl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_status","type":"uint256"}],"name":"removeStakeAndCloseTrove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_num","type":"uint256"}],"name":"setTroveStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tickInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalCollateralSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalStakesSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newBaseRate","type":"uint256"}],"name":"updateBaseRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IActivePool","name":"_activePool","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"updateSystemSnapshots_excludeCollRemainder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateTotalStakesAndSnapshot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"updateTroveCollAndStakeAndTotalStakes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"debt","type":"uint256"}],"name":"updateTroveDebt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"updateTroveRewardSnapshots","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_borrowers","type":"address[]"},{"internalType":"address[]","name":"_lowerHints","type":"address[]"},{"internalType":"address[]","name":"_upperHints","type":"address[]"}],"name":"updateTroves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_ids","type":"address[]"}],"name":"updateUnderCollateralizedTroves","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50615a2580620000216000396000f3fe608060405234801561001057600080fd5b50600436106103c35760003560e01c806378f8587611610206578063c7b554811161012b578063de665835116100c3578063f000543111610087578063f0005431146113ab578063f1201ae3146113d9578063f180b6f0146113e1578063f36b242514611407578063fefa753d1461140f576103c3565b8063de6658351461103d578063e1f1825014611063578063e2ac77b014611196578063e34f6d44146111bc578063e369e4ab146112ff576103c3565b8063c7b5548114610f38578063cde0479914610f40578063d380a37c14610f66578063d5b3563514610f6e578063d66a255314610f8b578063d894c9f814610fb1578063d9a7244414610fb9578063dc3fe76f14610ff2578063de16239114611020576103c3565b8063a3f4df7e1161019e578063a3f4df7e14610df5578063af3700dc14610e72578063b0cbb9bb14610e98578063b620115d14610ea0578063ba6e79f714610ea8578063bb40952214610ed6578063c52861f214610ede578063c617944514610ee6578063c622453514610f0c576103c3565b806378f8587614610c7e578063795d26c314610ca457806382fe3eb914610cac578063887105d314610cd25780638e201a1414610cda57806392edfc1514610d995780639972378414610da15780639cbf397914610dc7578063a20baee614610ded576103c3565b8063371e45ba116102ec57806350953b621161028457806350953b6214610b78578063584c3d2714610ba65780635d6b480f14610bda578063614e64d914610c06578063631203b014610c23578063657e7c6914610c4057806366ca4a2114610c48578063683c614214610c505780636b87d17614610c58576103c3565b8063371e45ba146109625780633735aa20146109a15780633bc8913e146109db5780634333837214610ae957806343a124fa14610b3b57806344936b1014610b435780634597997814610b4b578063477d66cf14610b5357806349eefeee14610b70576103c3565b806327fb7d891161035f57806327fb7d89146106385780632899aa4f1461065e57806328bd6ec1146107915780632b11551a146107ff5780632f8655681461080757806331bafd9b1461082d578063328e140414610859578063330283aa1461087f57806335a058b41461089c576103c3565b80630b076557146103c85780630e8307f8146103f057806312adf0c21461040a57806313312d591461056857806315d549f1146105b65780631e7ff8f6146105dc5780631f68f20a1461060257806321e378011461060a5780632409266914610630575b600080fd5b6103ee600480360360208110156103de57600080fd5b50356001600160a01b0316611435565b005b6103f861145d565b60408051918252519081900360200190f35b6104306004803603602081101561042057600080fd5b50356001600160a01b0316611469565b604051808881526020018060200180602001878152602001806020018060200186815260200185810385528b818151815260200191508051906020019060200280838360005b8381101561048e578181015183820152602001610476565b5050505090500185810384528a818151815260200191508051906020019060200280838360005b838110156104cd5781810151838201526020016104b5565b50505050905001858103835288818151815260200191508051906020019060200280838360005b8381101561050c5781810151838201526020016104f4565b50505050905001858103825287818151815260200191508051906020019060200280838360005b8381101561054b578181015183820152602001610533565b505050509050019b50505050505050505050505060405180910390f35b6103ee600480360360e081101561057e57600080fd5b508035906020810135906001600160a01b03604082013581169160608101358216916080820135169060a08101359060c001356115e7565b6103f8600480360360208110156105cc57600080fd5b50356001600160a01b03166116d9565b6103f8600480360360208110156105f257600080fd5b50356001600160a01b0316611784565b6103f861179f565b6103f86004803603602081101561062057600080fd5b50356001600160a01b03166117a5565b6103f86117d6565b6103f86004803603602081101561064e57600080fd5b50356001600160a01b03166117e1565b6103ee6004803603606081101561067457600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561069e57600080fd5b8201836020820111156106b057600080fd5b803590602001918460208302840111600160201b831117156106d157600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561072057600080fd5b82018360208201111561073257600080fd5b803590602001918460208302840111600160201b8311171561075357600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061180b945050505050565b6103ee600480360360208110156107a757600080fd5b810190602081018135600160201b8111156107c157600080fd5b8201836020820111156107d357600080fd5b803590602001918460208302840111600160201b831117156107f457600080fd5b5090925090506118eb565b6103f8611955565b6103ee6004803603602081101561081d57600080fd5b50356001600160a01b0316611967565b6103ee6004803603604081101561084357600080fd5b506001600160a01b038135169060200135611a1e565b6103f86004803603602081101561086f57600080fd5b50356001600160a01b0316611a45565b6103ee6004803603602081101561089557600080fd5b5035611a57565b6108c2600480360360208110156108b257600080fd5b50356001600160a01b0316611a9f565b604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b8381101561090c5781810151838201526020016108f4565b50505050905001838103825285818151815260200191508051906020019060200280838360005b8381101561094b578181015183820152602001610933565b505050509050019550505050505060405180910390f35b6109886004803603602081101561097857600080fd5b50356001600160a01b0316611aba565b6040805192835260208301919091528051918290030190f35b6109c7600480360360208110156109b757600080fd5b50356001600160a01b0316611d02565b604080519115158252519081900360200190f35b6103ee600480360360608110156109f157600080fd5b810190602081018135600160201b811115610a0b57600080fd5b820183602082011115610a1d57600080fd5b803590602001918460208302840111600160201b83111715610a3e57600080fd5b919390929091602081019035600160201b811115610a5b57600080fd5b820183602082011115610a6d57600080fd5b803590602001918460208302840111600160201b83111715610a8e57600080fd5b919390929091602081019035600160201b811115610aab57600080fd5b820183602082011115610abd57600080fd5b803590602001918460208302840111600160201b83111715610ade57600080fd5b509092509050611d38565b6103ee600480360360e0811015610aff57600080fd5b508035906020810135906001600160a01b0360408201358116916060810135821691608082013581169160a08101359160c09091013516611f61565b6109c761202e565b6103f8612038565b6103f8612044565b6103f860048036036020811015610b6957600080fd5b503561208f565b6103f86120a2565b6103f860048036036040811015610b8e57600080fd5b506001600160a01b03813581169160200135166120a8565b6103f860048036036060811015610bbc57600080fd5b506001600160a01b03813516906020810135906040013515156120d7565b6103ee60048036036040811015610bf057600080fd5b506001600160a01b03813516906020013561216f565b6103ee60048036036020811015610c1c57600080fd5b50356121c2565b6103f860048036036020811015610c3957600080fd5b50356121d9565b6103f86121e6565b6103f86121ec565b6103f86121fe565b6103ee60048036036020811015610c6e57600080fd5b50356001600160a01b031661220b565b6103f860048036036020811015610c9457600080fd5b50356001600160a01b0316612274565b6103f8612292565b6103ee60048036036020811015610cc257600080fd5b50356001600160a01b0316612398565b6103f86123b3565b610d0060048036036020811015610cf057600080fd5b50356001600160a01b0316612429565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015610d44578181015183820152602001610d2c565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015610d83578181015183820152602001610d6b565b5050505090500194505050505060405180910390f35b6103ee612450565b610d0060048036036020811015610db757600080fd5b50356001600160a01b0316612471565b6103f860048036036020811015610ddd57600080fd5b50356001600160a01b0316612545565b6103f8612557565b610dfd612563565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610e37578181015183820152602001610e1f565b50505050905090810190601f168015610e645780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6103f860048036036020811015610e8857600080fd5b50356001600160a01b031661258b565b6103ee61259d565b6103f86128f9565b6103f860048036036040811015610ebe57600080fd5b506001600160a01b0381358116916020013516612903565b6103f861292e565b6103f861293a565b6103f860048036036020811015610efc57600080fd5b50356001600160a01b031661294c565b6103ee60048036036040811015610f2257600080fd5b506001600160a01b038135169060200135612a26565b6103f8612da3565b6103f860048036036020811015610f5657600080fd5b50356001600160a01b0316612daf565b6103f8612dca565b6103f860048036036020811015610f8457600080fd5b5035612dd0565b6103f860048036036020811015610fa157600080fd5b50356001600160a01b0316612de3565b6103f8612e01565b610fd660048036036020811015610fcf57600080fd5b5035612e0e565b604080516001600160a01b039092168252519081900360200190f35b6103f86004803603604081101561100857600080fd5b506001600160a01b0381358116916020013516612e38565b6103f86004803603602081101561103657600080fd5b5035612e67565b6103f86004803603602081101561105357600080fd5b50356001600160a01b0316612ed7565b6103ee6004803603606081101561107957600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156110a357600080fd5b8201836020820111156110b557600080fd5b803590602001918460208302840111600160201b831117156110d657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561112557600080fd5b82018360208201111561113757600080fd5b803590602001918460208302840111600160201b8311171561115857600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612ee9945050505050565b6109c7600480360360208110156111ac57600080fd5b50356001600160a01b0316613123565b6103ee600480360360a08110156111d257600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b81111561120c57600080fd5b82018360208201111561121e57600080fd5b803590602001918460208302840111600160201b8311171561123f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561128e57600080fd5b8201836020820111156112a057600080fd5b803590602001918460208302840111600160201b831117156112c157600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061322e945050505050565b6103ee6004803603604081101561131557600080fd5b810190602081018135600160201b81111561132f57600080fd5b82018360208201111561134157600080fd5b803590602001918460208302840111600160201b8311171561136257600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505090356001600160a01b031691506132459050565b6103f8600480360360408110156113c157600080fd5b506001600160a01b038135811691602001351661329a565b6103f86132c9565b6103f8600480360360208110156113f757600080fd5b50356001600160a01b03166132cf565b6103f86132f0565b6103f86004803603602081101561142557600080fd5b50356001600160a01b03166132fd565b61143d613318565b60325460335461145a916001600160a01b03908116911683613364565b50565b6611c37937e080005b90565b6001600160a01b038116600090815260cf60205260408120600201546060908190839082908190839061149a6158ab565b6001600160a01b038a16600090815260cf6020908152604091829020825181546060938102820184018552938101848152909391928492849184018282801561150c57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116114ee575b505050505081526020016001820180548060200260200160405190810160405280929190818152602001828054801561156457602002820191906000526020600020905b815481526020019060010190808311611550575b505050505081525050905060008061157b8c611aba565b915091506115876158ab565b6115908d613874565b90506115b2826115a6878663ffffffff613a8f16565b9063ffffffff613a8f16565b94506115be8482613ad8565b805160209182015183519390920151969f909e50909c50929a5098509296509194509092505050565b6002609654141561162d576040805162461bcd60e51b815260206004820152601f60248201526000805160206159d0833981519152604482015290519081900360640190fd5b600260965560ca546040805163c2f6202d60e01b8152600481018a9052602481018990526001600160a01b0388811660448301528781166064830152868116608483015260a4820186905260c482018590523360e48301529151919092169163c2f6202d9161010480830192600092919082900301818387803b1580156116b357600080fd5b505af11580156116c7573d6000803e3d6000fd5b50506001609655505050505050505050565b60006116e3614314565b60d680546001808201835560008381527fe767803f8ecf1dee6bb0345811f7312cda556058b19db6389ad9ae3568643ddd90920180546001600160a01b0319166001600160a01b0387161790559154909161173e919061432e565b6001600160a01b038416600090815260cf602052604090206004018054610100600160881b0319166101006001600160801b03939093169283021790559150505b919050565b6001600160a01b0316600090815260d0602052604090205490565b60cd5481565b6001600160a01b038116600090815260cf6020526040812060049081015460ff16908111156117d057fe5b92915050565b66b1a2bc2ec5000081565b600060608060006117f18561435f565b9250925092506118028383836144bb565b95945050505050565b611813613318565b61182081518351146144d4565b6001600160a01b038316600090815260cf6020908152604091829020805483518184028101840190945280845261189c9387939092919083018282801561189057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611872575b5050505050848461450b565b6001600160a01b038316600090815260cf60209081526040822083518593859390916118d0916001850191908601906158c5565b505082516118e391906020850190615910565b505050505050565b60005b818110156119505761194883838381811061190557fe5b905060200201356001600160a01b0316670f43fc2c04ee000061194286868681811061192d57fe5b905060200201356001600160a01b03166117e1565b106147c7565b6001016118ee565b505050565b600061196260cd54614831565b905090565b600260965414156119ad576040805162461bcd60e51b815260206004820152601f60248201526000805160206159d0833981519152604482015290519081900360640190fd5b60026096556119bb8161485b565b6040805160018082528183019092526060916020808301908036833701905050905081816000815181106119eb57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050611a1581336148be565b50506001609655565b611a26613318565b6001600160a01b03909116600090815260cf6020526040902060020155565b60d76020526000908152604090205481565b611a5f613318565b60cd8190556040805182815290517fc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600c9181900360200190a161145a614957565b6060806000611aad8461435f565b9250925092509193909250565b60008060016001600160a01b038416600090815260cf6020526040902060049081015460ff1690811115611aea57fe5b14611afa57506000905080611cfd565b6001600160a01b038316600090815260cf602052604081206002810154909190611b3390680ad78ebc5ac620000063ffffffff61432e16565b6001600160a01b038616600090815260db6020526040812091925090815b8454811015611cd5576000858281548110611b6857fe5b60009182526020808320909101546001600160a01b031680835285825260408084205460da909352832054909350909190611ba9908363ffffffff61432e16565b90508015801590611bb957508115155b15611c11576001600160a01b0383166000908152600186016020526040812054611bfb908490611bef908563ffffffff6149ad16565b9063ffffffff614a0b16565b9050611c0d878263ffffffff613a8f16565b9650505b50506001600160a01b03808a16600090815260d5602090815260408083209385168352600190930181528282205460d490915291812054611c58908363ffffffff61432e16565b905080611c6757505050611ccd565b6001600160a01b03808c16600090815260cf602090815260408083209387168352600390930190529081205490611cb3611ca086614a38565b600a0a611bef848663ffffffff6149ad16565b9050611cc58c8263ffffffff613a8f16565b9b5050505050505b600101611b51565b508115611cf8576002810154611cf590611bef848663ffffffff6149ad16565b94505b505050505b915091565b600060016001600160a01b038316600090815260cf6020526040902060049081015460ff1690811115611d3157fe5b1492915050565b82611d4f8682148015611d4a57508183145b6144d4565b60608167ffffffffffffffff81118015611d6857600080fd5b50604051908082528060200260200182016040528015611d92578160200160208202803683370190505b509050611d9f6000614aa8565b60005b82811015611e0a576060806000611dd38c8c86818110611dbe57fe5b905060200201356001600160a01b031661435f565b925092509250611de4838383614e4f565b858581518110611df057fe5b602002602001018181525050505050806001019050611da2565b5060cc60009054906101000a90046001600160a01b03166001600160a01b0316638f37e2b88989848a8a8a8a6040518863ffffffff1660e01b8152600401808060200180602001806020018060200185810385528c8c828181526020019250602002808284376000838201819052601f909101601f191690920187810386528c5181528c51602091820193828f0193509102908190849084905b83811015611ebc578181015183820152602001611ea4565b505050509050018581038352898982818152602001925060200280828437600083820152601f01601f19169091018681038352878152602090810191508890880280828437600081840152601f19601f8201169050808301925050509b505050505050505050505050600060405180830381600087803b158015611f3f57600080fd5b505af1158015611f53573d6000803e3d6000fd5b505050505050505050505050565b60026096541415611fa7576040805162461bcd60e51b815260206004820152601f60248201526000805160206159d0833981519152604482015290519081900360640190fd5b600260965560ca5460408051630776fc9b60e21b8152600481018a9052602481018990526001600160a01b0388811660448301528781166064830152868116608483015260a4820186905284811660c48301523360e483015291519190921691631ddbf26c9161010480830192600092919082900301818387803b1580156116b357600080fd5b6000611962614e5c565b670f43fc2c04ee000090565b60008061204f614e75565b90506000612065670ddd4b8c6c7d70d883614e91565b9050612088670de0b6b3a7640000611bef8360cd546149ad90919063ffffffff16565b9250505090565b60006117d061209c6121ec565b83614f46565b60d65490565b6001600160a01b03918216600090815260d5602090815260408083209390941682526001909201909152205490565b60006120e1614314565b811561211a576001600160a01b038416600090815260cf6020526040902060020154612113908463ffffffff613a8f16565b9050612149565b6001600160a01b038416600090815260cf6020526040902060020154612146908463ffffffff61432e16565b90505b6001600160a01b03909316600090815260cf602052604090206002018390555090919050565b612177614314565b80600481111561218357fe5b6001600160a01b038316600090815260cf6020526040902060049081018054909160ff199091169060019084908111156121b957fe5b02179055505050565b6121ca614f64565b6121d46001614aa8565b60dd55565b60006117d061209c6132f0565b60dc5481565b60006119626121f9612044565b614f7e565b686194049f30f720000090565b612213614f64565b6001600160a01b038116600081815260da6020908152604091829020670de0b6b3a76400009081905582519384529083015280517f9e88ffa18257fce65e878a54543eaf3cabcbdfac0863488239a07711784f543f9281900390910190a150565b6001600160a01b0316600090815260db602052604090206002015490565b600080603260009054906101000a90046001600160a01b03166001600160a01b0316638df709926040518163ffffffff1660e01b815260040160206040518083038186803b1580156122e357600080fd5b505afa1580156122f7573d6000803e3d6000fd5b505050506040513d602081101561230d57600080fd5b5051603354604080516346fb84c960e11b815290519293506000926001600160a01b0390921691638df7099291600480820192602092909190829003018186803b15801561235a57600080fd5b505afa15801561236e573d6000803e3d6000fd5b505050506040513d602081101561238457600080fd5b50519050612088828263ffffffff613a8f16565b6123a0614314565b6123aa6000614aa8565b61145a81615019565b60325460408051631944d03160e21b815290516000926001600160a01b03169163651340c4916004808301926020929190829003018186803b1580156123f857600080fd5b505afa15801561240c573d6000803e3d6000fd5b505050506040513d602081101561242257600080fd5b5051905090565b6060806124346158ab565b61243d84613874565b8051602090910151909350915050915091565b600080546001600160a01b0316331415612468575060015b61145a81614aa8565b6001600160a01b038116600090815260cf602090815260409182902080548351818402810184019094528084526060938493600184019284918301828280156124e357602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116124c5575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561253557602002820191906000526020600020905b815481526020019060010190808311612521575b5050505050905091509150915091565b60d86020526000908152604090205481565b670de0b6b3a764000081565b6040518060400160405280600c81526020016b2a3937bb32a6b0b730b3b2b960a11b81525081565b60d16020526000908152604090205481565b6f0e8bb18028d78047a15f79ed3511c56533146125b957600080fd5b6000805460408051634eb5750560e11b815290516060936001600160a01b0390931692639d6aea0a9260048082019391829003018186803b1580156125fd57600080fd5b505afa158015612611573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561263a57600080fd5b8101908080516040519392919084600160201b82111561265957600080fd5b90830190602082018581111561266e57600080fd5b82518660208202830111600160201b8211171561268a57600080fd5b82525081516020918201928201910280838360005b838110156126b757818101518382015260200161269f565b50505050919091016040819052603254630e0905d360e21b82526020600483018181528851602485015288519899506060986001600160a01b039093169750633824174c96508995509350839260440191818601910280838360005b8381101561272b578181015183820152602001612713565b505050509050019250505060006040518083038186803b15801561274e57600080fd5b505afa158015612762573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561278b57600080fd5b8101908080516040519392919084600160201b8211156127aa57600080fd5b9083019060208201858111156127bf57600080fd5b82518660208202830111600160201b821117156127db57600080fd5b82525081516020918201928201910280838360005b838110156128085781810151838201526020016127f0565b50505050905001604052505050905060005b825181101561195057600083828151811061283157fe5b6020908102919091018101516001600160a01b038116600090815260d2835260408082205460d185529181902091909155805142815290519193507f523f4c1c183d46bec16040588c9c0aee1cbce44d14a4499539c18ef5761b6c3d92908290030190a18282815181106128a157fe5b6020908102919091018101516001600160a01b038316600081815260d0845260409081902083905580519182529281019190915281516000805160206159b0833981519152929181900390910190a15060010161281a565b6000611962615166565b6001600160a01b03918216600090815260d56020908152604080832093909416825291909152205490565b6714d1120d7b16000090565b6000611962612947612044565b614831565b6001600160a01b038116600090815260cf602090815260408083208151815460609481028201850184529281018381526117d094919384928491908401828280156129c057602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116129a2575b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015612a1857602002820191906000526020600020905b815481526020019060010190808311612a04575b5050505050815250506151f2565b612a2e613318565b612a398260006147c7565b612a416158ab565b6001600160a01b038316600090815260cf60205260408120805490915b81811015612b48576000838281548110612a7457fe5b6000918252602080832091909101546001600160a01b038a811680855260d5845260408086209290931680865282855283862086905560019092018452828520859055845260cf835281842081855260030183528184205460d090935292205491925090612ae2908261432e565b6001600160a01b03928316600081815260d06020908152604080832094909455948b1680825260cf86528382208383526003018652838220829055815260db85528281209181528185528281208190556001918201909452908320929092555001612a5e565b506001600160a01b038516600090815260db6020526040812060020155836004811115612b7157fe5b6001600160a01b038616600090815260cf6020526040902060049081018054909160ff19909116906001908490811115612ba757fe5b02179055506001600160a01b038516600090815260cf60209081526040909120845180518693612bdb928492910190615910565b506020828101518051612bf492600185019201906158c5565b5050506001600160a01b038516600090815260cf602052604081206002015560d654612c1f816152f4565b6001600160a01b038616600090815260cf602052604081206004015461010090046001600160801b03169060d6612c5784600161432e565b81548110612c6157fe5b60009182526020909120015460d680546001600160a01b03909216925082916001600160801b038516908110612c9357fe5b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905591831680825260cf83526040918290206004018054610100600160881b0319166101006001600160801b0388169081029190911790915582519182529281019290925280517f02b04ae5f7be9ca7c103293a2aa15f3c339d15d6eda53b721fef7b0e609c831a9281900390910190a160d6805480612d3557fe5b600082815260208120820160001990810180546001600160a01b031916905590910190915560cc5460408051631484968760e11b81526001600160a01b038c81166004830152915192909116926329092d0e9260248084019382900301818387803b158015611f3f57600080fd5b670ddd4b8c6c7d70d881565b6001600160a01b0316600090815260d4602052604090205490565b60ce5481565b60006117d0612ddd61293a565b8361532d565b6001600160a01b0316600090815260cf602052604090206002015490565b680ad78ebc5ac620000090565b600060d68281548110612e1d57fe5b6000918252602090912001546001600160a01b031692915050565b6001600160a01b03918216600090815260cf602090815260408083209390941682526003909201909152205490565b6000612e71614314565b6000612e7b612044565b60cd8190556040805182815290519192507fc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600c919081900360200190a1612ebf614957565b612ed0612eca6132f0565b84614f46565b9392505050565b60d26020526000908152604090205481565b612ef1613318565b6060836001600160a01b0316633824174c846040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019060200280838360005b83811015612f52578181015183820152602001612f3a565b505050509050019250505060006040518083038186803b158015612f7557600080fd5b505afa158015612f89573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015612fb257600080fd5b8101908080516040519392919084600160201b821115612fd157600080fd5b908301906020820185811115612fe657600080fd5b82518660208202830111600160201b8211171561300257600080fd5b82525081516020918201928201910280838360005b8381101561302f578181015183820152602001613017565b50505050905001604052505050905060005b83518110156130e957600084828151811061305857fe5b6020908102919091018101516001600160a01b038116600090815260d0835260408082205460d190945290209190915584519091506130c79085908490811061309d57fe5b60200260200101518484815181106130b157fe5b602002602001015161432e90919063ffffffff16565b6001600160a01b03909116600090815260d26020526040902055600101613041565b506040805142815290517f523f4c1c183d46bec16040588c9c0aee1cbce44d14a4499539c18ef5761b6c3d9181900360200190a150505050565b600060016001600160a01b038316600090815260cf6020526040902060049081015460ff169081111561315257fe5b1461315f5750600061177f565b6001600160a01b038216600090815260cf60205260408120905b815481101561322457600082828154811061319057fe5b60009182526020808320909101546001600160a01b0390811680845260d38352604080852054928a16855260d584528085208286529093529190922054909250108061320a57506001600160a01b03808216600081815260da6020908152604080832054948a16835260db82528083209383529290522054105b1561321b576001935050505061177f565b50600101613179565b5060009392505050565b613236613318565b815181516103c39082146144d4565b6002609654141561328b576040805162461bcd60e51b815260206004820152601f60248201526000805160206159d0833981519152604482015290519081900360640190fd5b6002609655611a1582826148be565b6001600160a01b03918216600090815260db602090815260408083209390941682526001909201909152205490565b60dd5481565b600060608060006132df8561435f565b925092509250611802838383614e4f565b600061196260cd54614f7e565b6001600160a01b0316600090815260d3602052604090205490565b60c8546001600160a01b0316331480159061333e575060ca546001600160a01b03163314155b8015613355575060cb546001600160a01b03163314155b1561336257613362615386565b565b61336e6000614aa8565b61337781613123565b15611950576133858161485b565b61338d6158ab565b61339682613874565b90506000806133a484611aba565b6001600160a01b038616600090815260cf6020908152604091829020825181546060938102820184018552938101848152959750939550613486949092849284919084018282801561341f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613401575b505050505081526020016001820180548060200260200160405190810160405280929190818152602001828054801561347757602002820191906000526020600020905b815481526020019060010190808311613463575b50505050508152505084613ad8565b6001600160a01b038516600090815260cf602090815260409091208251805191926134b692849290910190615910565b5060208281015180516134cf92600185019201906158c5565b5050506001600160a01b038416600090815260cf60205260409020600201546135049082906115a6908563ffffffff613a8f16565b6001600160a01b03808616600090815260cf60205260408082206002019390935560c854835163d8c1ec8960e01b815260048101869052935192169263d8c1ec89926024808301939282900301818387803b15801561356257600080fd5b505af1158015613576573d6000803e3d6000fd5b5050505061358384615019565b846001600160a01b031663e7b1d678836040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156135c957600080fd5b505af11580156135dd573d6000803e3d6000fd5b5050506001600160a01b038716905063420429ca6135fb8484613a8f565b6040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561363157600080fd5b505af1158015613645573d6000803e3d6000fd5b50508451602080870151604080516308b2135760e11b8152600481019182528451604482015284516001600160a01b038d16975063116426ae9650929391928392602483019260640191818801910280838360005b838110156136b257818101518382015260200161369a565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156136f15781810151838201526020016136d9565b50505050905001945050505050600060405180830381600087803b15801561371857600080fd5b505af115801561372c573d6000803e3d6000fd5b5050604080516001600160a01b03881681526020810185905281517f11883dbbc997d02c733f213f8862794f7634669925e006376cee371143442ca19450908190039091019150a16001600160a01b038416600081815260cf602090815260408083206002810154825181815260608101869052608094810185815283549582018690527f98c96bbf8d2aa6be678259387225d169084fca483ed9598bde8ea258036bcacf969295939460018601949083019060a08401908790801561381b57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116137fd575b5050838103825285818154815260200191508054801561385a57602002820191906000526020600020905b815481526020019060010190808311613846575b5050965050505050505060405180910390a2505050505050565b61387c6158ab565b60016001600160a01b038316600090815260cf6020526040902060049081015460ff16908111156138a957fe5b146138bd576138b66158ab565b905061177f565b6001600160a01b038216600090815260cf602090815260409182902080548351818402810184019094528084526060939283018282801561392757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613909575b505050505090506000815190508067ffffffffffffffff8111801561394b57600080fd5b50604051908082528060200260200182016040528015613975578160200160208202803683370190505b50602084015281835260005b81811015613a8757600083828151811061399757fe5b6020908102919091018101516001600160a01b03808916600090815260d58452604080822092841682529184528181205460d390945290812054919350906139e5908363ffffffff61432e16565b905080613a12576000876020015185815181106139fe57fe5b602002602001018181525050505050613a7f565b613a5f613a1e84614a38565b6001600160a01b03808b16600090815260cf602090815260408083209389168352600390930190522054600a9190910a90611bef908463ffffffff6149ad16565b87602001518581518110613a6f57fe5b6020026020010181815250505050505b600101613981565b505050919050565b600082820183811015612ed0576040805162461bcd60e51b815260206004820152600c60248201526b616464206f766572666c6f7760a01b604482015290519081900360640190fd5b613ae06158ab565b81515183515181613af55784925050506117d0565b80613b045783925050506117d0565b613b0c6158ab565b82820167ffffffffffffffff81118015613b2557600080fd5b50604051908082528060200260200182016040528015613b4f578160200160208202803683370190505b50815281830167ffffffffffffffff81118015613b6b57600080fd5b50604051908082528060200260200182016040528015613b95578160200160208202803683370190505b50602080830191909152600080548851604051632d79b8eb60e01b8152600481018581528251602483015282519495869586956060956001600160a01b0390911694632d79b8eb9491939192839260449092019185810191028083838c5b83811015613c0b578181015183820152602001613bf3565b505050509050019250505060006040518083038186803b158015613c2e57600080fd5b505afa158015613c42573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015613c6b57600080fd5b8101908080516040519392919084600160201b821115613c8a57600080fd5b908301906020820185811115613c9f57600080fd5b82518660208202830111600160201b82111715613cbb57600080fd5b82525081516020918201928201910280838360005b83811015613ce8578181015183820152602001613cd0565b50505050905001604052505050905060606000809054906101000a90046001600160a01b03166001600160a01b0316632d79b8eb8b600001516040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019060200280838360005b83811015613d70578181015183820152602001613d58565b505050509050019250505060006040518083038186803b158015613d9357600080fd5b505afa158015613da7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015613dd057600080fd5b8101908080516040519392919084600160201b821115613def57600080fd5b908301906020820185811115613e0457600080fd5b82518660208202830111600160201b82111715613e2057600080fd5b82525081516020918201928201910280838360005b83811015613e4d578181015183820152602001613e35565b5050505090500160405250505090506000828681518110613e6a57fe5b602002602001015190506000828681518110613e8257fe5b602002602001015190505b80821015613f33578c51805188908110613ea357fe5b602002602001015188600001518681518110613ebb57fe5b6001600160a01b039092166020928302919091018201528d0151805188908110613ee157fe5b602002602001015188602001518681518110613ef957fe5b60200260200101818152505086600101965088871415613f18576140d7565b838781518110613f2457fe5b602002602001015191506140cc565b81811015613fd9578b51805187908110613f4957fe5b602002602001015188600001518681518110613f6157fe5b6001600160a01b039092166020928302919091018201528c0151805187908110613f8757fe5b602002602001015188602001518681518110613f9f57fe5b60200260200101818152505085600101955089861415613fbe576140d7565b828681518110613fca57fe5b602002602001015190506140cc565b8c51805188908110613fe757fe5b602002602001015188600001518681518110613fff57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250506140608c60200151878151811061403257fe5b60200260200101518e60200151898151811061404a57fe5b6020026020010151613a8f90919063ffffffff16565b8860200151868151811061407057fe5b6020026020010181815250508660010196508560010195508887148061409557508986145b1561409f576140d7565b8387815181106140ab57fe5b602002602001015191508286815181106140c157fe5b602002602001015190505b846001019450613e8d565b8460010194505b88871015614167578c518051889081106140f457fe5b60200260200101518860000151868151811061410c57fe5b6001600160a01b039092166020928302919091018201528d015180518890811061413257fe5b60200260200101518860200151868151811061414a57fe5b6020026020010181815250508660010196508460010194506140de565b898610156141f0578b5180518790811061417d57fe5b60200260200101518860000151868151811061419557fe5b6001600160a01b039092166020928302919091018201528c01518051879081106141bb57fe5b6020026020010151886020015186815181106141d357fe5b602002602001018181525050856001019550846001019450614167565b60608567ffffffffffffffff8111801561420957600080fd5b50604051908082528060200260200182016040528015614233578160200160208202803683370190505b50905060608667ffffffffffffffff8111801561424f57600080fd5b50604051908082528060200260200182016040528015614279578160200160208202803683370190505b509050600098505b868910156142fc57895180518a90811061429757fe5b6020026020010151828a815181106142ab57fe5b6001600160a01b039092166020928302919091018201528a015180518a9081106142d157fe5b6020026020010151818a815181106142e557fe5b602002602001018181525050886001019850614281565b908c5260208c01525050505050505050505092915050565b60c8546001600160a01b0316331461336257613362615386565b6000612ed083836040518060400160405280600c81526020016b737562206f766572666c6f7760a01b8152506153b9565b606080600061436c6158ab565b61437585613874565b905060008061438387611aba565b6001600160a01b038916600090815260cf6020526040812060020154929450909250906143bc9083906115a6908663ffffffff613a8f16565b90506143c66158ab565b6001600160a01b038916600090815260cf60209081526040918290208251815460609381028201840185529381018481526144a19491938492849184018282801561443a57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161441c575b505050505081526020016001820180548060200260200160405190810160405280929190818152602001828054801561449257602002820191906000526020600020905b81548152602001906001019080831161447e575b50505050508152505086613ad8565b8051602090910151909a9099509197509095505050505050565b6000806144c88585615450565b90506118028184615550565b8061145a576040805162461bcd60e51b8152602060048201526002602482015261313160f01b604482015290519081900360640190fd5b81516001600160a01b038516600090815260db6020526040812090805b8651811015614643578382101580614578575086818151811061454757fe5b60200260200101516001600160a01b031686838151811061456457fe5b60200260200101516001600160a01b031614155b1561463457600087828151811061458b57fe5b6020908102919091018101516001600160a01b0380821660008181526001890185526040808220829055928e16815260cf85528281209181526003909101845281812080549082905560d090945220549092506145e8908261432e565b6001600160a01b038316600081815260d06020908152604091829020849055815192835282019290925281516000805160206159b0833981519152929181900390910190a1505061463b565b8160010191505b600101614528565b5060606146508686615588565b600092509050815b848310156147b557600087848151811061466e57fe5b6020026020010151905060006146978289878151811061468a57fe5b602002602001015161572a565b6001600160a01b038c8116600090815260cf6020908152604080832093871683526003909301815282822080549085905560d090915291902054919250906146e59083906115a6908461432e565b6001600160a01b038416600090815260d06020526040902055845185908790811061470c57fe5b6020026020010151876001016000856001600160a01b03166001600160a01b031681526020019081526020016000208190555061476585878151811061474e57fe5b602002602001015185613a8f90919063ffffffff16565b6001600160a01b038416600081815260d060209081526040918290205482519384529083015280519296506000805160206159b083398151915292918290030190a1505050826001019250614658565b60029093019290925550505050505050565b60cc546040805163265eb84f60e01b81526001600160a01b03858116600483015284151560248301529151919092169163265eb84f91604480830192600092919082900301818387803b15801561481d57600080fd5b505af11580156118e3573d6000803e3d6000fd5b60006117d061484d6611c37937e080008463ffffffff613a8f16565b670de0b6b3a7640000615789565b60016001600160a01b038216600090815260cf6020526040902060049081015460ff169081111561488857fe5b1461145a576040805162461bcd60e51b81526020600482015260016024820152603960f81b604482015290519081900360640190fd5b6148c86000614aa8565b60cb546040805163e369e4ab60e01b81526001600160a01b0384811660248301526004820192835285516044830152855193169263e369e4ab92869286928291606401906020808701910280838360005b83811015614931578181015183820152602001614919565b505050509050019350505050600060405180830381600087803b15801561481d57600080fd5b603c61496e60ce544261432e90919063ffffffff16565b10613362574260ce81905560408051918252517f860f8d2f0c74dd487e89e2883e3b25b8159ce1e1b3433a291cba7b82c508f3bc9181900360200190a1565b6000826149bc575060006117d0565b828202828482816149c957fe5b0414612ed0576040805162461bcd60e51b815260206004820152600c60248201526b6d756c206f766572666c6f7760a01b604482015290519081900360640190fd5b6000612ed0838360405180604001604052806008815260200167064697620627920360c41b81525061579f565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015614a7357600080fd5b505afa158015614a87573d6000803e3d6000fd5b505050506040513d6020811015614a9d57600080fd5b505160ff1692915050565b6000614abf60dc544261432e90919063ffffffff16565b90508180614acf575060dd548110155b15614e4b576000805460408051634eb5750560e11b815290516060936001600160a01b0390931692639d6aea0a9260048082019391829003018186803b158015614b1857600080fd5b505afa158015614b2c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015614b5557600080fd5b8101908080516040519392919084600160201b821115614b7457600080fd5b908301906020820185811115614b8957600080fd5b82518660208202830111600160201b82111715614ba557600080fd5b82525081516020918201928201910280838360005b83811015614bd2578181015183820152602001614bba565b5050505091909101604081905260008054632fc86b6760e21b8352602060048401818152895160248601528951999a506060996001600160a01b03909316985063bf21ad9c97508a9650945084936044019281870192909102908190849084905b83811015614c4b578181015183820152602001614c33565b505050509050019250505060006040518083038186803b158015614c6e57600080fd5b505afa158015614c82573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015614cab57600080fd5b8101908080516040519392919084600160201b821115614cca57600080fd5b908301906020820185811115614cdf57600080fd5b82518660208202830111600160201b82111715614cfb57600080fd5b82525081516020918201928201910280838360005b83811015614d28578181015183820152602001614d10565b50505050905001604052505050905060005b8251811015614e4357818181518110614d4f57fe5b6020026020010151600014614e3b576000838281518110614d6c57fe5b60200260200101519050614de2670de0b6b3a7640000611bef614dbd670de0b6b3a76400006115a660dd54611bef8c8b8b81518110614da757fe5b60200260200101516149ad90919063ffffffff16565b6001600160a01b038516600090815260da60205260409020549063ffffffff6149ad16565b6001600160a01b038216600081815260da6020908152604091829020849055815192835282019290925281517f9e88ffa18257fce65e878a54543eaf3cabcbdfac0863488239a07711784f543f929181900390910190a1505b600101614d3a565b50504260dc55505b5050565b6000806144c88585615804565b60006714d1120d7b160000614e6f615166565b10905090565b6000611962603c611bef60ce544261432e90919063ffffffff16565b6000631f540500821115614ea757631f54050091505b81614ebb5750670de0b6b3a76400006117d0565b670de0b6b3a764000083835b6001811115614f325760028106614efc57614ee2828361586a565b9150614ef581600263ffffffff614a0b16565b9050614f2d565b614f06828461586a565b9250614f12828361586a565b9150614f2a6002611bef83600163ffffffff61432e16565b90505b614ec7565b614f3c828461586a565b9695505050505050565b6000612ed0670de0b6b3a7640000611bef858563ffffffff6149ad16565b6000546001600160a01b0316331461336257613362615386565b60006117d061500c836000809054906101000a90046001600160a01b03166001600160a01b0316635e9e32126040518163ffffffff1660e01b815260040160206040518083038186803b158015614fd457600080fd5b505afa158015614fe8573d6000803e3d6000fd5b505050506040513d6020811015614ffe57600080fd5b50519063ffffffff613a8f16565b66b1a2bc2ec50000615789565b6001600160a01b038116600090815260cf602090815260409182902080548351818402810184019094528084526060939283018282801561508357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311615065575b5050506001600160a01b038516600090815260db602052604081209394509150505b825181101561512d5760008382815181106150bc57fe5b6020908102919091018101516001600160a01b03908116600081815260d38452604080822054938a16825260d585528082209282528285528082209390935560d4845282812054600192830185528382205560da8452828120549387905291909120919091559190910190506150a5565b506040805142815290517f82bbdd533bb4dbd06fe175bb28e15f2391ffb6124331ff8f7066153c385baacb9181900360200190a1505050565b6032546040805163896a008960e01b8152815160009384936001600160a01b039091169263896a00899260048083019392829003018186803b1580156151ab57600080fd5b505afa1580156151bf573d6000803e3d6000fd5b505050506040513d60408110156151d557600080fd5b5060200151905060006151e6612292565b90506120888282615550565b600080548251602080850151604080516397a4b9f760e01b8152600481019182528451604482015284516001600160a01b03909616956397a4b9f795948392602481019260649091019180880191028083838d5b8381101561525e578181015183820152602001615246565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561529d578181015183820152602001615285565b5050505090500194505050505060206040518083038186803b1580156152c257600080fd5b505afa1580156152d6573d6000803e3d6000fd5b505050506040513d60208110156152ec57600080fd5b505192915050565b6001811161145a576040805162461bcd60e51b81526020600482015260016024820152603760f81b604482015290519081900360640190fd5b60008061534c670de0b6b3a7640000611bef868663ffffffff6149ad16565b9050828110612ed0576040805162461bcd60e51b81526020600482015260016024820152603760f81b604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600360248201526257464360e81b604482015290519081900360640190fd5b600081848411156154485760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561540d5781810151838201526020016153f5565b50505050905090810190601f16801561543a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b60008054604080516397a4b9f760e01b8152600481019182528551604482015285516001600160a01b03909316926397a4b9f7928792879282916024810191606490910190602080880191028083838d5b838110156154b95781810151838201526020016154a1565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156154f85781810151838201526020016154e0565b5050505090500194505050505060206040518083038186803b15801561551d57600080fd5b505afa158015615531573d6000803e3d6000fd5b505050506040513d602081101561554757600080fd5b50519392505050565b6000811561557f57600061557683611bef86670de0b6b3a764000063ffffffff6149ad16565b91506117d09050565b506000196117d0565b600080546040805163a055852360e01b8152600481019182528551604482015285516060946001600160a01b039094169363a055852393889388939192839260248301926064019160208089019202908190849084905b838110156155f75781810151838201526020016155df565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561563657818101518382015260200161561e565b5050505090500194505050505060006040518083038186803b15801561565b57600080fd5b505afa15801561566f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561569857600080fd5b8101908080516040519392919084600160201b8211156156b757600080fd5b9083019060208201858111156156cc57600080fd5b82518660208202830111600160201b821117156156e857600080fd5b82525081516020918201928201910280838360005b838110156157155781810151838201526020016156fd565b50505050905001604052505050905092915050565b6001600160a01b038216600090815260d2602052604081205461574e5750806117d0565b6001600160a01b038316600090815260d2602090815260408083205460d190925290912054612ed09190611bef90859063ffffffff6149ad16565b60008183106157985781612ed0565b5090919050565b600081836157ee5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561540d5781810151838201526020016153f5565b5060008385816157fa57fe5b0495945050505050565b600080546040805163617ddfff60e01b8152600481019182528551604482015285516001600160a01b039093169263617ddfff928792879282916024810191606490910190602080880191028083838d83156154b95781810151838201526020016154a1565b60008061587d848463ffffffff6149ad16565b90506158a3670de0b6b3a7640000611bef836706f05b59d3b2000063ffffffff613a8f16565b949350505050565b604051806040016040528060608152602001606081525090565b828054828255906000526020600020908101928215615900579160200282015b828111156159005782518255916020019190600101906158e5565b5061590c929150615971565b5090565b828054828255906000526020600020908101928215615965579160200282015b8281111561596557825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190615930565b5061590c92915061598b565b61146691905b8082111561590c5760008155600101615977565b61146691905b8082111561590c5780546001600160a01b031916815560010161599156fed5630907014190fb9340da1cf427c96f094dabfbf73d25506e4b0cda9e3a4ca05265656e7472616e637947756172643a207265656e7472616e742063616c6c00a26469706673582212201ef22dd72a98be15dcb6213b09cf641dc83350ee5770d2fb3a35477317b0ce6764736f6c634300060b0033

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.