Contract 0x58777f4C737AA87d2a895486587Eb6eCcd8d5eFa

Contract Overview

Balance:
0 AVAX
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x186e1de7d370330ca15b12293fb14be60416ac7a20e6dae500edae46866573deSet Addresses60673082022-02-14 1:31:01235 days 3 hrs ago0x9e97ae5d91da840eed47a5b07fdabbc2bfad8ada IN 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0 AVAX0.0153603 45
0xe77d121cb0b54bf36ae5fdfe50d1a338276738d3356ff09ac0930724d5b42aec0x6080604060665662022-02-14 1:05:40235 days 4 hrs ago0x9e97ae5d91da840eed47a5b07fdabbc2bfad8ada IN  Create: TroveManagerRedemptions0 AVAX0.19055475 45
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x0db336c1ab0cc14972b53caa4f100068731c54aa0 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x0db336c1ab0cc14972b53caa4f100068731c54aa0 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x6a62e076f10ecd61ca91a456bf86d40fdb8bcc650 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x6a62e076f10ecd61ca91a456bf86d40fdb8bcc650 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x6a62e076f10ecd61ca91a456bf86d40fdb8bcc650 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x05671a0c758aaed71d759b41091c67457dc8882e0 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x1aa108e2392cd316be9255f78a3673e9f6f654470 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x1aa108e2392cd316be9255f78a3673e9f6f654470 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x1aa108e2392cd316be9255f78a3673e9f6f654470 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x05671a0c758aaed71d759b41091c67457dc8882e0 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xcb74a8f05537e346ac54c173652e57c96b4339d20 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x05671a0c758aaed71d759b41091c67457dc8882e0 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x6a62e076f10ecd61ca91a456bf86d40fdb8bcc650 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0xf7d924a8f414a16a54f2cf9a98ac266281239ffb0 AVAX
0xb5fcd175f383bfd7207b27dba8331e0a74634307d65bfa9d9c9121edd6be916970968112022-03-09 23:04:29211 days 6 hrs ago 0x58777f4c737aa87d2a895486587eb6eccd8d5efa0x0db336c1ab0cc14972b53caa4f100068731c54aa0 AVAX
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
TroveManagerRedemptions

Compiler Version
v0.6.11+commit.5ef660b1

Optimization Enabled:
Yes with 100 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 28 : TroveManagerRedemptions.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./Interfaces/IWAsset.sol";
import "./Dependencies/TroveManagerBase.sol";

/** 
 * TroveManagerRedemptions is derived from TroveManager and handles all redemption activity of troves. 
 * Instead of calculating redemption fees in ETH like Liquity used to, we now calculate it as a portion 
 * of YUSD passed in to redeem. The YUSDAmount is still how much we would like to redeem, but the 
 * YUSDFee is now the maximum amount of YUSD extra that will be paid and must be in the balance of the 
 * redeemer for the redemption to succeed. This fee is the same as before in terms of percentage of value, 
 * but now it is in terms of YUSD. We now use a helper function to be able to estimate how much YUSD will 
 * be actually needed to perform a redemption of a certain amount, and also given an amount of YUSD balance,
 * the max amount of YUSD that can be used for a redemption, and a max fee such that it will always go through. 
 * 
 * Given a balance of YUSD, Z, the amount that can actually be redeemed is : 
 * Y = YUSD you can actually redeem
 * BR = decayed base rate 
 * X = YUSD Fee
 * S = Total YUSD Supply
 * The redemption fee rate is = (Y / S * 1 / BETA + BR + 0.5%)
 * This is because the new base rate = BR + Y / S * 1 / BETA
 * We pass in X + Y = Z, and want to find X and Y. 
 * Y is calculated to be = S * (sqrt((1.005 + BR)**2 + BETA * Z / S) - 1.005 - BR)
 * through the quadratic formula, and X = Z - Y. 
 * Therefore the amount we can actually redeem given Z is Y, and the max fee is X. 
 * 
 * To find how much the fee is given Y, we can multiply Y by the new base rate, which is BR + Y / S * 1 / BETA. 
 * 
 * To the redemption function, we pass in Y and X. 
 */

contract TroveManagerRedemptions is TroveManagerBase {
    struct RedemptionTotals {
        uint256 remainingYUSD;
        uint256 totalYUSDToRedeem;
        newColls CollsDrawn;
        uint256 YUSDfee;
        uint256 decayedBaseRate;
        uint256 totalYUSDSupplyAtStart;
        uint256 maxYUSDFeeAmount;
    }
    struct Hints {
        address upper;
        address lower;
        address target;
        uint256 icr;
    }

    /*
     * BETA: 18 digit decimal. Parameter by which to divide the redeemed fraction, in order to calc the new base rate from a redemption.
     * Corresponds to (1 / ALPHA) in the white paper.
     */
    uint256 public constant BETA = 2;

    uint256 public constant BOOTSTRAP_PERIOD = 14 days;

    event Redemption(
        uint256 _attemptedYUSDAmount,
        uint256 _actualYUSDAmount,
        uint256 YUSDfee,
        address[] tokens,
        uint256[] amounts
    );

    function setAddresses(
        address _borrowerOperationsAddress,
        address _activePoolAddress,
        address _defaultPoolAddress,
        address _stabilityPoolAddress,
        address _gasPoolAddress,
        address _collSurplusPoolAddress,
        address _yusdTokenAddress,
        address _sortedTrovesAddress,
        address _yetiTokenAddress,
        address _sYETIAddress,
        address _whitelistAddress,
        address _troveManagerAddress
    ) external onlyOwner {
        checkContract(_borrowerOperationsAddress);
        checkContract(_activePoolAddress);
        checkContract(_defaultPoolAddress);
        checkContract(_stabilityPoolAddress);
        checkContract(_gasPoolAddress);
        checkContract(_collSurplusPoolAddress);
        checkContract(_yusdTokenAddress);
        checkContract(_sortedTrovesAddress);
        checkContract(_yetiTokenAddress);
        checkContract(_sYETIAddress);
        checkContract(_whitelistAddress);
        checkContract(_troveManagerAddress);

        borrowerOperationsAddress = _borrowerOperationsAddress;
        activePool = IActivePool(_activePoolAddress);
        defaultPool = IDefaultPool(_defaultPoolAddress);
        stabilityPoolContract = IStabilityPool(_stabilityPoolAddress);
        whitelist = IWhitelist(_whitelistAddress);
        gasPoolAddress = _gasPoolAddress;
        collSurplusPool = ICollSurplusPool(_collSurplusPoolAddress);
        yusdTokenContract = IYUSDToken(_yusdTokenAddress);
        sortedTroves = ISortedTroves(_sortedTrovesAddress);
        yetiTokenContract = IYETIToken(_yetiTokenAddress);
        sYETIContract = ISYETI(_sYETIAddress);
        troveManager = ITroveManager(_troveManagerAddress);

        emit BorrowerOperationsAddressChanged(_borrowerOperationsAddress);
        emit ActivePoolAddressChanged(_activePoolAddress);
        emit DefaultPoolAddressChanged(_defaultPoolAddress);
        emit StabilityPoolAddressChanged(_stabilityPoolAddress);
        emit GasPoolAddressChanged(_gasPoolAddress);
        emit CollSurplusPoolAddressChanged(_collSurplusPoolAddress);
        emit YUSDTokenAddressChanged(_yusdTokenAddress);
        emit SortedTrovesAddressChanged(_sortedTrovesAddress);
        emit YETITokenAddressChanged(_yetiTokenAddress);
        emit SYETIAddressChanged(_sYETIAddress);

        _renounceOwnership();
    }

    /** 
     * Main function for redeeming collateral. See above for how YUSDMaxFee is calculated.
     * @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
     * _YUSDamount + _YUSDMaxFee must be less than the balance of the sender.
     */
    function redeemCollateral(
        uint256 _YUSDamount,
        uint256 _YUSDMaxFee,
        address _firstRedemptionHint,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintICR,
        uint256 _maxIterations,
        address _redeemer
    ) external {
        _requireCallerisTroveManager();
        ContractsCache memory contractsCache = ContractsCache(
            activePool,
            defaultPool,
            yusdTokenContract,
            sYETIContract,
            sortedTroves,
            collSurplusPool,
            gasPoolAddress
        );
        RedemptionTotals memory totals;

        _requireValidMaxFee(_YUSDamount, _YUSDMaxFee);
        _requireAfterBootstrapPeriod();
        _requireTCRoverMCR();
        _requireAmountGreaterThanZero(_YUSDamount);

        totals.totalYUSDSupplyAtStart = getEntireSystemDebt();

        // Confirm redeemer's balance is less than total YUSD supply
        assert(contractsCache.yusdToken.balanceOf(_redeemer) <= totals.totalYUSDSupplyAtStart);

        totals.remainingYUSD = _YUSDamount;
        address currentBorrower;
        if (_isValidFirstRedemptionHint(contractsCache.sortedTroves, _firstRedemptionHint)) {
            currentBorrower = _firstRedemptionHint;
        } else {
            currentBorrower = contractsCache.sortedTroves.getLast();
            // Find the first trove with ICR >= MCR
            while (
                currentBorrower != address(0) && troveManager.getCurrentICR(currentBorrower) < MCR
            ) {
                currentBorrower = contractsCache.sortedTroves.getPrev(currentBorrower);
            }
        }
        // Loop through the Troves starting from the one with lowest collateral ratio until _amount of YUSD is exchanged for collateral
        if (_maxIterations == 0) {
            _maxIterations = uint256(-1);
        }
        while (currentBorrower != address(0) && totals.remainingYUSD > 0 && _maxIterations > 0) {
            _maxIterations--;
            // Save the address of the Trove preceding the current one, before potentially modifying the list
            address nextUserToCheck = contractsCache.sortedTroves.getPrev(currentBorrower);

            if (troveManager.getCurrentICR(currentBorrower) >= MCR) {
                troveManager.applyPendingRewards(currentBorrower);

                SingleRedemptionValues memory singleRedemption = _redeemCollateralFromTrove(
                    contractsCache,
                    _redeemer,
                    currentBorrower,
                    totals.remainingYUSD,
                    _upperPartialRedemptionHint,
                    _lowerPartialRedemptionHint,
                    _partialRedemptionHintICR
                );

                if (singleRedemption.cancelledPartial) break; // Partial redemption was cancelled (out-of-date hint, or new net debt < minimum), therefore we could not redeem from the last Trove

                totals.totalYUSDToRedeem = totals.totalYUSDToRedeem.add(singleRedemption.YUSDLot); 

                totals.CollsDrawn = _sumColls(totals.CollsDrawn, singleRedemption.CollLot);
                totals.remainingYUSD = totals.remainingYUSD.sub(singleRedemption.YUSDLot);
            }

            currentBorrower = nextUserToCheck;
        }

        require(isNonzero(totals.CollsDrawn));
        // Decay the baseRate due to time passed, and then increase it according to the size of this redemption.
        // Use the saved total YUSD supply value, from before it was reduced by the redemption.
        _updateBaseRateFromRedemption(totals.totalYUSDToRedeem, totals.totalYUSDSupplyAtStart);

        totals.YUSDfee = _getRedemptionFee(totals.totalYUSDToRedeem);
        // check user has enough YUSD to pay fee and redemptions
        _requireYUSDBalanceCoversRedemption(
            contractsCache.yusdToken,
            _redeemer,
            _YUSDamount.add(totals.YUSDfee)
        );

        // check to see that the fee doesn't exceed the max fee
        _requireUserAcceptsFeeRedemption(totals.YUSDfee, _YUSDMaxFee);

        // send fee from user to YETI stakers
        contractsCache.yusdToken.transferFrom(
            _redeemer,
            address(contractsCache.sYETI),
            totals.YUSDfee
        );

        emit Redemption(
            _YUSDamount,
            totals.totalYUSDToRedeem,
            totals.YUSDfee,
            totals.CollsDrawn.tokens,
            totals.CollsDrawn.amounts
        );
        // Burn the total YUSD that is cancelled with debt
        contractsCache.yusdToken.burn(_redeemer, totals.totalYUSDToRedeem);
        // Update Active Pool YUSD, and send Collaterals to account
        contractsCache.activePool.decreaseYUSDDebt(totals.totalYUSDToRedeem);

        contractsCache.activePool.sendCollateralsUnwrap(
            _redeemer,
            totals.CollsDrawn.tokens,
            totals.CollsDrawn.amounts,
            false
        );
    }

    /** 
     * Secondary function for redeeming collateral. See above for how YUSDMaxFee is calculated.
     * @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
     * _YUSDamount + _YUSDMaxFee must be less than the balance of the sender.
     */
    function redeemCollateralSingle(
        uint256 _YUSDamount,
        uint256 _YUSDMaxFee,
        address _firstRedemptionHint,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintICR,
        address _collToRedeem
    ) external {
        // _requireCallerisTroveManager();
        ContractsCache memory contractsCache = ContractsCache(
            activePool,
            defaultPool,
            yusdTokenContract,
            sYETIContract,
            sortedTroves,
            collSurplusPool,
            gasPoolAddress
        );
        RedemptionTotals memory totals;
        Hints memory hints;

        hints.target=_firstRedemptionHint;
        hints.icr=_partialRedemptionHintICR;
        hints.upper=_upperPartialRedemptionHint;
        hints.lower=_lowerPartialRedemptionHint;
        
        _requireValidMaxFee(_YUSDamount, _YUSDMaxFee);
        _requireAfterBootstrapPeriod();
        _requireTCRoverMCR();
        _requireAmountGreaterThanZero(_YUSDamount);
        // address _redeemer = msg.sender;
        totals.totalYUSDSupplyAtStart = getEntireSystemDebt();

        // Confirm redeemer's balance is less than total YUSD supply
        assert(contractsCache.yusdToken.balanceOf(msg.sender) <= totals.totalYUSDSupplyAtStart);

        totals.remainingYUSD = _YUSDamount;
        require(_isValidFirstRedemptionHint(contractsCache.sortedTroves, hints.target), "Invalid first redemption hint");
        require(troveManager.getCurrentICR(hints.target) >= MCR, "Trove is underwater");
        troveManager.applyPendingRewards(hints.target);

        // SingleRedemptionValues memory singleRedemption = _redeemCollateralFromTrove(
        //     contractsCache,
        //     _redeemer,
        //     currentBorrower,
        //     totals.remainingYUSD,
        //     _upperPartialRedemptionHint,
        //     _lowerPartialRedemptionHint,
        //     _partialRedemptionHintICR
        // );


        // Stitched in _redeemCollateralFromTrove
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        SingleRedemptionValues memory singleRedemption;
        // Determine the remaining amount (lot) to be redeemed, capped by the entire debt of the Trove minus the liquidation reserve
        uint troveDebt=troveManager.getTroveDebt(hints.target);
        singleRedemption.YUSDLot = LiquityMath._min(
            totals.remainingYUSD,
            troveDebt.sub(YUSD_GAS_COMPENSATION)
        );

        newColls memory colls;
        (colls.tokens, colls.amounts, ) = troveManager.getCurrentTroveState(hints.target);

        uint256 i; //FYI: i term will be used as the index of the collateral to redeem later too
        
        {//Limit scope
            //Make sure single collateral to redeem exists in trove
            bool foundCollateral;
            
            for (i = 0; i < colls.tokens.length; i++) {
                if (colls.tokens[i] == _collToRedeem) {
                    foundCollateral = true;
                    break;
                }
            }
            require(foundCollateral, "Collateral to redeem not found in trove");
        }

        {// Limit scope
            uint256 singleCollUSD = whitelist.getValueUSD(_collToRedeem, colls.amounts[i]); //Get usd value of only the collateral being redeemed
            
            //Cap redemption amount to the max amount of collateral that can be redeemed
            singleRedemption.YUSDLot = LiquityMath._min(
                singleCollUSD,
                singleRedemption.YUSDLot
            );
            

            // redemption addresses are the same as coll addresses for trove
            // Calculation for how much collateral to send of each type. 
            singleRedemption.CollLot.tokens = colls.tokens;
            singleRedemption.CollLot.amounts = new uint256[](colls.tokens.length);
            
            uint tokenAmountToRedeem = singleRedemption.YUSDLot.mul(colls.amounts[i]).div(singleCollUSD);
            colls.amounts[i] = colls.amounts[i].sub(tokenAmountToRedeem);
            singleRedemption.CollLot.amounts[i] = tokenAmountToRedeem;
            // if it is a wrapped asset we need to reduce reward. 
            // Later the asset will be transferred directly out, so no new reward is needed to be kept track of
            if (whitelist.isWrapped(colls.tokens[i])) {
                IWAsset(colls.tokens[i]).updateReward(hints.target, msg.sender, tokenAmountToRedeem);
            }
        }

        
        // Decrease the debt and collateral of the current Trove according to the YUSD lot and corresponding Collateral to send
        uint256 newDebt = troveDebt.sub(singleRedemption.YUSDLot);
        

        if (newDebt == YUSD_GAS_COMPENSATION) {
            // No debt left in the Trove (except for the liquidation reserve), therefore the trove gets closed
            troveManager.removeStakeTMR(hints.target);
            troveManager.closeTroveRedemption(hints.target);
            _redeemCloseTrove(
                contractsCache,
                hints.target,
                YUSD_GAS_COMPENSATION,
                colls.tokens,
                colls.amounts
            );

            address[] memory emptyTokens = new address[](0);
            uint256[] memory emptyAmounts = new uint256[](0);

            emit TroveUpdated(
                hints.target,
                0,
                emptyTokens,
                emptyAmounts,
                TroveManagerOperation.redeemCollateral
            );
        } else {
            
            uint256 newICR = LiquityMath._computeCR(_getVC(colls.tokens, colls.amounts), newDebt);

            /*
            * If the provided hint is too inaccurate of date, we bail since trying to reinsert without a good hint will almost
            * certainly result in running out of gas. Arbitrary measures of this mean newICR must be greater than hint ICR - 2%, 
            * and smaller than hint ICR + 2%.
            *
            * If the resultant net debt of the partial is less than the minimum, net debt we bail.
            */
            {//Stack scope
                if (newICR >= hints.icr.add(2e16) || 
                    newICR <= hints.icr.sub(2e16) || 
                    _getNetDebt(newDebt) < MIN_NET_DEBT) {
                    revert("Invalid partial redemption hint or remaining debt is too low");
                    // singleRedemption.cancelledPartial = true;
                    // return singleRedemption;
                }
            
                contractsCache.sortedTroves.reInsert(
                    hints.target,
                    newICR,
                    hints.upper,
                    hints.lower
                );
            }
            troveManager.updateTroveDebt(hints.target, newDebt);
            // for (uint256 k = 0; k < colls.tokens.length; k++) {
            //     colls.amounts[k] = finalAmounts[k];
            // }
            troveManager.updateTroveCollTMR(hints.target, colls.tokens, colls.amounts);
            troveManager.updateStakeAndTotalStakes(hints.target);

            emit TroveUpdated(
                hints.target,
                newDebt,
                colls.tokens,
                colls.amounts,
                TroveManagerOperation.redeemCollateral
            );
        }
    
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////


        totals.totalYUSDToRedeem = singleRedemption.YUSDLot; 

        totals.CollsDrawn = singleRedemption.CollLot;
        // totals.remainingYUSD = totals.remainingYUSD.sub(singleRedemption.YUSDLot);

        require(isNonzero(totals.CollsDrawn));
        // Decay the baseRate due to time passed, and then increase it according to the size of this redemption.
        // Use the saved total YUSD supply value, from before it was reduced by the redemption.
        _updateBaseRateFromRedemption(totals.totalYUSDToRedeem, totals.totalYUSDSupplyAtStart);

        totals.YUSDfee = _getRedemptionFee(totals.totalYUSDToRedeem);
        // check user has enough YUSD to pay fee and redemptions
        _requireYUSDBalanceCoversRedemption(
            contractsCache.yusdToken,
            msg.sender,
            totals.remainingYUSD.add(totals.YUSDfee)
        );

        // check to see that the fee doesn't exceed the max fee
        _requireUserAcceptsFeeRedemption(totals.YUSDfee, _YUSDMaxFee);

        // send fee from user to YETI stakers
        contractsCache.yusdToken.transferFrom(
            msg.sender,
            address(contractsCache.sYETI),
            totals.YUSDfee
        );

        emit Redemption(
            totals.remainingYUSD,
            totals.totalYUSDToRedeem,
            totals.YUSDfee,
            totals.CollsDrawn.tokens,
            totals.CollsDrawn.amounts
        );
        // Burn the total YUSD that is cancelled with debt
        contractsCache.yusdToken.burn(msg.sender, totals.totalYUSDToRedeem);
        // Update Active Pool YUSD, and send Collaterals to account
        contractsCache.activePool.decreaseYUSDDebt(totals.totalYUSDToRedeem);

        contractsCache.activePool.sendCollateralsUnwrap(
            msg.sender,
            totals.CollsDrawn.tokens,
            totals.CollsDrawn.amounts,
            false
        );
    }

    /** 
     * Redeem as much collateral as possible from _borrower's Trove in exchange for YUSD up to _maxYUSDamount
     * Special calculation for determining how much collateral to send of each type to send. 
     * We want to redeem equivalent to the USD value instead of the VC value here, so we take the YUSD amount
     * which we are redeeming from this trove, and calculate the ratios at which we would redeem a single 
     * collateral type compared to all others. 
     * For example if we are redeeming 10,000 from this trove, and it has collateral A with a safety ratio of 1, 
     * collateral B with safety ratio of 0.5. Let's say their price is each 1. The trove is composed of 10,000 A and 
     * 10,000 B, so we would redeem 5,000 A and 5,000 B, instead of 6,666 A and 3,333 B. To do calculate this we take 
     * the USD value of that collateral type, and divide it by the total USD value of all collateral types. The price 
     * actually cancels out here so we just do YUSD amount * token amount / total USD value, instead of
     * YUSD amount * token value / total USD value / token price, since we are trying to find token amount.
     */
    function _redeemCollateralFromTrove(
        ContractsCache memory _contractsCache,
        address _redeemCaller,
        address _borrower,
        uint256 _maxYUSDAmount,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintICR
    ) internal returns (SingleRedemptionValues memory singleRedemption) {
        // Determine the remaining amount (lot) to be redeemed, capped by the entire debt of the Trove minus the liquidation reserve
        singleRedemption.YUSDLot = LiquityMath._min(
            _maxYUSDAmount,
            troveManager.getTroveDebt(_borrower).sub(YUSD_GAS_COMPENSATION)
        );

        newColls memory colls;
        (colls.tokens, colls.amounts, ) = troveManager.getCurrentTroveState(_borrower);

        uint256[] memory finalAmounts = new uint256[](colls.tokens.length);

        uint256 totalCollUSD = _getUSDColls(colls);
        uint256 baseLot = singleRedemption.YUSDLot.mul(DECIMAL_PRECISION);

        // redemption addresses are the same as coll addresses for trove
        // Calculation for how much collateral to send of each type. 
        singleRedemption.CollLot.tokens = colls.tokens;
        singleRedemption.CollLot.amounts = new uint256[](colls.tokens.length);
        for (uint256 i = 0; i < colls.tokens.length; i++) {
            uint tokenAmountToRedeem = baseLot.mul(colls.amounts[i]).div(totalCollUSD).div(1e18);
            finalAmounts[i] = colls.amounts[i].sub(tokenAmountToRedeem);
            singleRedemption.CollLot.amounts[i] = tokenAmountToRedeem;
            // if it is a wrapped asset we need to reduce reward. 
            // Later the asset will be transferred directly out, so no new reward is needed to be kept track of
            if (whitelist.isWrapped(colls.tokens[i])) {
                IWAsset(colls.tokens[i]).updateReward(_borrower, _redeemCaller, tokenAmountToRedeem);
            }
        }

        // Decrease the debt and collateral of the current Trove according to the YUSD lot and corresponding Collateral to send
        uint256 newDebt = (troveManager.getTroveDebt(_borrower)).sub(singleRedemption.YUSDLot);
        uint256 newColl = _getVC(colls.tokens, finalAmounts); // VC given newAmounts in trove

        if (newDebt == YUSD_GAS_COMPENSATION) {
            // No debt left in the Trove (except for the liquidation reserve), therefore the trove gets closed
            troveManager.removeStakeTMR(_borrower);
            troveManager.closeTroveRedemption(_borrower);
            _redeemCloseTrove(
                _contractsCache,
                _borrower,
                YUSD_GAS_COMPENSATION,
                colls.tokens,
                finalAmounts
            );

            address[] memory emptyTokens = new address[](0);
            uint256[] memory emptyAmounts = new uint256[](0);

            emit TroveUpdated(
                _borrower,
                0,
                emptyTokens,
                emptyAmounts,
                TroveManagerOperation.redeemCollateral
            );
        } else {
            uint256 newICR = LiquityMath._computeCR(newColl, newDebt);

            /*
             * If the provided hint is too inaccurate of date, we bail since trying to reinsert without a good hint will almost
             * certainly result in running out of gas. Arbitrary measures of this mean newICR must be greater than hint ICR - 2%, 
             * and smaller than hint ICR + 2%.
             *
             * If the resultant net debt of the partial is less than the minimum, net debt we bail.
             */

            if (newICR >= _partialRedemptionHintICR.add(2e16) || 
                newICR <= _partialRedemptionHintICR.sub(2e16) || 
                _getNetDebt(newDebt) < MIN_NET_DEBT) {
                singleRedemption.cancelledPartial = true;
                return singleRedemption;
            }

            _contractsCache.sortedTroves.reInsert(
                _borrower,
                newICR,
                _upperPartialRedemptionHint,
                _lowerPartialRedemptionHint
            );

            troveManager.updateTroveDebt(_borrower, newDebt);
            for (uint256 i = 0; i < colls.tokens.length; i++) {
                colls.amounts[i] = finalAmounts[i];
            }
            troveManager.updateTroveCollTMR(_borrower, colls.tokens, colls.amounts);
            troveManager.updateStakeAndTotalStakes(_borrower);

            emit TroveUpdated(
                _borrower,
                newDebt,
                colls.tokens,
                finalAmounts,
                TroveManagerOperation.redeemCollateral
            );
        }

        return singleRedemption;
    }

    /*
     * Called when a full redemption occurs, and closes the trove.
     * The redeemer swaps (debt - liquidation reserve) YUSD for (debt - liquidation reserve) worth of Collateral, so the YUSD liquidation reserve left corresponds to the remaining debt.
     * In order to close the trove, the YUSD liquidation reserve is burned, and the corresponding debt is removed from the active pool.
     * The debt recorded on the trove's struct is zero'd elswhere, in _closeTrove.
     * Any surplus Collateral left in the trove, is sent to the Coll surplus pool, and can be later claimed by the borrower.
     */
    function _redeemCloseTrove(
        ContractsCache memory _contractsCache,
        address _borrower,
        uint256 _YUSD,
        address[] memory _remainingColls,
        uint256[] memory _remainingCollsAmounts
    ) internal {
        _contractsCache.yusdToken.burn(gasPoolAddress, _YUSD);
        // Update Active Pool YUSD, and send Collateral to account
        _contractsCache.activePool.decreaseYUSDDebt(_YUSD);

        // send Collaterals from Active Pool to CollSurplus Pool
        _contractsCache.collSurplusPool.accountSurplus(
            _borrower,
            _remainingColls,
            _remainingCollsAmounts
        );
        _contractsCache.activePool.sendCollaterals(
            address(_contractsCache.collSurplusPool),
            _remainingColls,
            _remainingCollsAmounts
        );
    }

    /*
     * This function has two impacts on the baseRate state variable:
     * 1) decays the baseRate based on time passed since last redemption or YUSD borrowing operation.
     * then,
     * 2) increases the baseRate based on the amount redeemed, as a proportion of total supply
     */
    function _updateBaseRateFromRedemption(uint256 _YUSDDrawn, uint256 _totalYUSDSupply)
        internal
        returns (uint256)
    {
        uint256 decayedBaseRate = troveManager.calcDecayedBaseRate();

        /* Convert the drawn Collateral back to YUSD at face value rate (1 YUSD:1 USD), in order to get
         * the fraction of total supply that was redeemed at face value. */
        uint256 redeemedYUSDFraction = _YUSDDrawn.mul(10**18).div(_totalYUSDSupply);

        uint256 newBaseRate = decayedBaseRate.add(redeemedYUSDFraction.div(BETA));
        newBaseRate = LiquityMath._min(newBaseRate, DECIMAL_PRECISION); // cap baseRate at a maximum of 100%

        troveManager.updateBaseRate(newBaseRate);
        return newBaseRate;
    }

    function _isValidFirstRedemptionHint(ISortedTroves _sortedTroves, address _firstRedemptionHint)
        internal
        view
        returns (bool)
    {
        if (
            _firstRedemptionHint == address(0) ||
            !_sortedTroves.contains(_firstRedemptionHint) ||
            troveManager.getCurrentICR(_firstRedemptionHint) < MCR
        ) {
            return false;
        }

        address nextTrove = _sortedTroves.getNext(_firstRedemptionHint);
        return nextTrove == address(0) || troveManager.getCurrentICR(nextTrove) < MCR;
    }

    function _requireUserAcceptsFeeRedemption(uint256 _actualFee, uint256 _maxFee) internal pure {
        require(_actualFee <= _maxFee, "User must accept fee");
    }

    function _requireValidMaxFee(uint256 _YUSDAmount, uint256 _maxYUSDFee) internal pure {
        uint256 _maxFeePercentage = _maxYUSDFee.mul(DECIMAL_PRECISION).div(_YUSDAmount);
        require(_maxFeePercentage >= REDEMPTION_FEE_FLOOR, "Max fee must be at least 0.5%");
        require(_maxFeePercentage <= DECIMAL_PRECISION, "Max fee must be at most 100%");
    }

    function _requireAfterBootstrapPeriod() internal view {
        uint256 systemDeploymentTime = yetiTokenContract.getDeploymentStartTime();
        require(
            block.timestamp >= systemDeploymentTime.add(BOOTSTRAP_PERIOD),
            "TroveManager: Redemptions are not allowed during bootstrap phase"
        );
    }

    function _requireTCRoverMCR() internal view {
        require(_getTCR() >= MCR, "TroveManager: Cannot redeem when TCR < MCR");
    }

    function _requireAmountGreaterThanZero(uint256 _amount) internal pure {
        require(_amount > 0, "TroveManager: Amount must be greater than zero");
    }

    function _requireYUSDBalanceCoversRedemption(
        IYUSDToken _yusdToken,
        address _redeemer,
        uint256 _amount
    ) internal view {
        require(
            _yusdToken.balanceOf(_redeemer) >= _amount,
            "TroveManager: Requested redemption amount must be <= user's YUSD token balance"
        );
    }

    function isNonzero(newColls memory coll) internal pure returns (bool) {
        for (uint256 i = 0; i < coll.amounts.length; i++) {
            if (coll.amounts[i] > 0) {
                return true;
            }
        }
        return false;
    }

    function _requireCallerisTroveManager() internal view {
        require(msg.sender == address(troveManager));
    }

    function _getRedemptionFee(uint256 _YUSDRedeemed) internal view returns (uint256) {
        return _calcRedemptionFee(troveManager.getRedemptionRate(), _YUSDRedeemed);
    }

    function _calcRedemptionFee(uint256 _redemptionRate, uint256 _YUSDRedeemed)
        internal
        pure
        returns (uint256)
    {
        uint256 redemptionFee = _redemptionRate.mul(_YUSDRedeemed).div(DECIMAL_PRECISION);
        require(
            redemptionFee < _YUSDRedeemed,
            "TroveManager: Fee would eat up all returned collateral"
        );
        return redemptionFee;
    }

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

File 2 of 28 : IWAsset.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;


// Wrapped Asset
interface IWAsset  {

    function wrap(uint _amount, address _from, address _to, address _rewardOwner) external;

    function unwrapFor(address _for, uint amount) external;

    function updateReward(address from, address to, uint amount) external;

    function claimReward(address _to) external;

    function claimRewardFor(address _for) external;

    function getPendingRewards(address _for) external returns (address[] memory tokens, uint[] memory amounts);

    function endTreasuryReward(uint _amount) external;
}

File 3 of 28 : 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/ISYETI.sol";
import "../Interfaces/IActivePool.sol";
import "../Interfaces/IWhitelist.sol";
import "../Interfaces/ITroveManagerLiquidations.sol";
import "../Interfaces/ITroveManagerRedemptions.sol";
import "./LiquityBase.sol";
import "./Ownable.sol";
import "./CheckContract.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, Ownable, CheckContract {

    // --- Connected contract declarations ---

    address public borrowerOperationsAddress;

    IStabilityPool stabilityPoolContract;

    ITroveManager public troveManager;

    IYUSDToken yusdTokenContract;

    IYETIToken yetiTokenContract;

    ISYETI sYETIContract;

    ITroveManagerRedemptions troveManagerRedemptions;

    ITroveManagerLiquidations troveManagerLiquidations;

    address gasPoolAddress;

    address public troveManagerAddress;
    address public troveManagerRedemptionsAddress;
    address public troveManagerLiquidationsAddress;

    // A doubly linked list of Troves, sorted by their sorted by their individual collateral ratios
    ISortedTroves public sortedTroves;

    ICollSurplusPool collSurplusPool;

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

    struct SingleRedemptionValues {
        uint YUSDLot;
        newColls CollLot;
        bool cancelledPartial;
    }

    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 BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
    event YUSDTokenAddressChanged(address _newYUSDTokenAddress);
    event ActivePoolAddressChanged(address _activePoolAddress);
    event DefaultPoolAddressChanged(address _defaultPoolAddress);
    event StabilityPoolAddressChanged(address _stabilityPoolAddress);
    event GasPoolAddressChanged(address _gasPoolAddress);
    event CollSurplusPoolAddressChanged(address _collSurplusPoolAddress);
    event SortedTrovesAddressChanged(address _sortedTrovesAddress);
    event YETITokenAddressChanged(address _yetiTokenAddress);
    event SYETIAddressChanged(address _sYETIAddress);

    event TroveUpdated(address indexed _borrower, uint _debt, address[] _tokens, uint[] _amounts, TroveManagerOperation operation);
}

File 4 of 28 : ITroveManager.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

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


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

    // --- Events ---

    event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
    event PriceFeedAddressChanged(address _newPriceFeedAddress);
    event YUSDTokenAddressChanged(address _newYUSDTokenAddress);
    event ActivePoolAddressChanged(address _activePoolAddress);
    event DefaultPoolAddressChanged(address _defaultPoolAddress);
    event StabilityPoolAddressChanged(address _stabilityPoolAddress);
    event GasPoolAddressChanged(address _gasPoolAddress);
    event CollSurplusPoolAddressChanged(address _collSurplusPoolAddress);
    event SortedTrovesAddressChanged(address _sortedTrovesAddress);
    event YETITokenAddressChanged(address _yetiTokenAddress);
    event SYETIAddressChanged(address _sYETIAddress);

    event Liquidation(uint liquidatedAmount, uint totalYUSDGasCompensation, 
        address[] totalCollTokens, uint[] totalCollAmounts,
        address[] totalCollGasCompTokens, uint[] totalCollGasCompAmounts);
    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 _stabilityPoolAddress,
        address _gasPoolAddress,
        address _collSurplusPoolAddress,
        address _yusdTokenAddress,
        address _sortedTrovesAddress,
        address _yetiTokenAddress,
        address _sYETIAddress,
        address _whitelistAddress,
        address _troveManagerRedemptionsAddress,
        address _troveManagerLiquidationsAddress
    )
    external;

    function stabilityPool() external view returns (IStabilityPool);
    function yusdToken() external view returns (IYUSDToken);
    function yetiToken() external view returns (IYETIToken);
    function sYETI() external view returns (ISYETI);

    function getTroveOwnersCount() external view returns (uint);

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

    function getCurrentICR(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 updateStakeAndTotalStakes(address _borrower) external;

    function updateTroveCollTMR(address  _borrower, address[] memory addresses, uint[] memory amounts) external;

    function updateTroveRewardSnapshots(address _borrower) external;

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

    function applyPendingRewards(address _borrower) external;

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

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

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

//    function getEntireDebtAndColl(address _borrower) external view returns (
//        uint debt,
//        uint coll,
//        uint pendingYUSDDebtReward,
//        uint pendingETHReward
//    );

    function closeTrove(address _borrower) external;

    function removeStake(address _borrower) external;

    function removeStakeTMR(address _borrower) 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 decayBaseRateFromBorrowing() external;

    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 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);

    // returns the VC value of a trove
    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 updateTroveColl(address _borrower, address[] memory _tokens, uint[] memory _amounts) external;

    function increaseTroveDebt(address _borrower, uint _debtIncrease) external returns (uint);

    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 removeStakeTLR(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);

    function movePendingTroveRewardsToActivePool(IActivePool _activePool, IDefaultPool _defaultPool, uint _YUSD, address[] memory _tokens, uint[] memory _amounts, address _borrower) external;

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

}

File 5 of 28 : 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 BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
    event TroveManagerAddressChanged(address _newTroveManagerAddress);
    event ActivePoolAddressChanged(address _newActivePoolAddress);
    event DefaultPoolAddressChanged(address _newDefaultPoolAddress);
    event YUSDTokenAddressChanged(address _newYUSDTokenAddress);
    event SortedTrovesAddressChanged(address _newSortedTrovesAddress);
    event PriceFeedAddressChanged(address _newPriceFeedAddress);
    event CommunityIssuanceAddressChanged(address _newCommunityIssuanceAddress);

    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 FrontEndRegistered(address indexed _frontEnd, uint _kickbackRate);
    event FrontEndTagSet(address indexed _depositor, address indexed _frontEnd);

    event DepositSnapshotUpdated(address indexed _depositor, uint _P, uint _S, uint _G);
    event FrontEndSnapshotUpdated(address indexed _frontEnd, uint _P, uint _G);
    event UserDepositChanged(address indexed _depositor, uint _newDeposit);
    event FrontEndStakeChanged(address indexed _frontEnd, uint _newFrontEndStake, address _depositor);

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

    // --- Functions ---

    /*
     * Called only once on init, to set addresses of other Liquity 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 _whitelistAddress,
        address _troveManagerLiquidationsAddress
    )
        external;

    /*
     * Initial checks:
     * - Frontend is registered or zero address
     * - Sender is not a registered frontend
     * - _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, address _frontEndTag) 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;


    /*
     * Initial checks:
     * - Frontend (sender) not already registered
     * - User (sender) has no deposit
     * - _kickbackRate is in the range [0, 100%]
     * ---
     * Front end makes a one-time selection of kickback rate upon registering
     */
    function registerFrontEnd(uint _kickbackRate) external;

    /*
     * 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;

//    /*
//     * Returns the total amount of ETH held by the pool, accounted in an internal variable instead of `balance`,
//     * to exclude edge cases like ETH received from a self-destruct.
//     */
//    function getETH() external view returns (uint);
    
     //*
//     * 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 YETI gain earned by the front end.
     */
    function getFrontEndYETIGain(address _frontEnd) external view returns (uint);

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

    /*
     * Return the front end's compounded stake.
     *
     * The front end's compounded stake is equal to the sum of its depositors' compounded deposits.
     */
    function getCompoundedFrontEndStake(address _frontEnd) 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);

}

File 6 of 28 : ICollSurplusPool.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

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


interface ICollSurplusPool is ICollateralReceiver {

    // --- Events ---
    
    event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
    event TroveManagerAddressChanged(address _newTroveManagerAddress);
    event ActivePoolAddressChanged(address _newActivePoolAddress);

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

    // --- Contract setters ---

    function setAddresses(
        address _borrowerOperationsAddress,
        address _troveManagerAddress,
        address _troveManagerRedemptionsAddress,
        address _activePoolAddress,
        address _whitelistAddress
    ) external;

    function getCollVC() external view returns (uint);

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

    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 claimColl(address _account) external;

    function addCollateralType(address _collateral) external;
}

File 7 of 28 : 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 TroveManagerAddressChanged(address _troveManagerAddress);
    event StabilityPoolAddressChanged(address _newStabilityPoolAddress);
    event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);

    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;
}

File 8 of 28 : ISortedTroves.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

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

    // --- Events ---
    
    event SortedTrovesAddressChanged(address _sortedDoublyLLAddress);
    event BorrowerOperationsAddressChanged(address _borrowerOperationsAddress);
    event NodeAdded(address _id, uint _NICR);
    event NodeRemoved(address _id);

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

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

    function remove(address _id) external;

    function reInsert(address _id, uint256 _newICR, address _prevId, address _nextId) 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 getNext(address _id) external view returns (address);

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

    function getOldICR(address _id) 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);
}

File 9 of 28 : 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 10 of 28 : ISYETI.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

interface ISYETI {
    /**
     * @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);

    function mint(uint256 amount) external returns (bool);
    function burn(address to, uint256 shares) external returns (bool);

    /**
     * @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 11 of 28 : IActivePool.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./IPool.sol";

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

    // --- Functions ---
    
    function sendCollaterals(address _to, address[] memory _tokens, uint[] memory _amounts) external returns (bool);
    function sendCollateralsUnwrap(
        address _to,
        address[] memory _tokens,
        uint[] memory _amounts,
        bool _collectRewards) external returns (bool);
    function getCollateralVC(address collateralAddress) external view returns (uint);
    function addCollateralType(address _collateral) external;
}

File 12 of 28 : IWhitelist.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;


interface IWhitelist {
    function getValidCollateral() view external returns (address[] memory);

    function setAddresses(
        address _activePoolAddress,
        address _defaultPoolAddress,
        address _stabilityPoolAddress,
        address _collSurplusPoolAddress, 
        address _borrowerOperationsAddress
    ) external;

    function isValidRouter(address _router) external view returns (bool);
    function getOracle(address _collateral) view external returns (address);
    function getRatio(address _collateral) view external returns (uint256);
    function getIsActive(address _collateral) view external returns (bool);
    function getPriceCurve(address _collateral) external view returns (address);
    function getDecimals(address _collateral) external view returns (uint256);
    function getFee(address _collateral, uint _collateralVCInput, uint256 _collateralVCBalancePost, uint256 _totalVCBalancePre, uint256 _totalVCBalancePost) external view returns (uint256 fee);
    function getFeeAndUpdate(address _collateral, uint _collateralVCInput, uint256 _collateralVCBalancePost, uint256 _totalVCBalancePre, uint256 _totalVCBalancePost) external returns (uint256 fee);
    function getIndex(address _collateral) external view returns (uint256);
    function isWrapped(address _collateral) external view returns (bool);
    function setDefaultRouter(address _collateral, address _router) external;

    function getValueVC(address _collateral, uint _amount) view external returns (uint);
    function getValueUSD(address _collateral, uint _amount) view external returns (uint256);
    function getDefaultRouterAddress(address _collateral) external view returns (address);
}

File 13 of 28 : ITroveManagerLiquidations.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;


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

File 14 of 28 : 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,
        // uint _maxFeePercentage,
        address _redeemSender
    )
    external;
}

File 15 of 28 : LiquityBase.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./LiquityMath.sol";
import "../Interfaces/IActivePool.sol";
import "../Interfaces/IDefaultPool.sol";
import "../Interfaces/ILiquityBase.sol";
import "../Interfaces/IWhitelist.sol";
import "./YetiCustomBase.sol";


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

    uint constant public _100pct = 1000000000000000000; // 1e18 == 100%

    uint constant public _110pct = 1100000000000000000; // 1.1e18 == 110%

    // Minimum collateral ratio for individual troves
    uint constant public MCR = 1100000000000000000; // 110%

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

    // Amount of YUSD to be locked in gas pool on opening troves
    uint constant public YUSD_GAS_COMPENSATION = 200e18;

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

    uint constant public PERCENT_DIVISOR = 200; // dividing by 200 yields 0.5%

    uint constant public BORROWING_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5; // 0.5%
    uint constant public REDEMPTION_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5; // 0.5%

    IActivePool public activePool;

    IDefaultPool public defaultPool;

    // --- Gas compensation functions ---

    // Returns the composite debt (drawn debt + gas compensation) of a trove, for the purpose of ICR calculation
    function _getCompositeDebt(uint _debt) internal pure returns (uint) {
        return _debt.add(YUSD_GAS_COMPENSATION);
    }


    function _getNetDebt(uint _debt) internal pure returns (uint) {
        return _debt.sub(YUSD_GAS_COMPENSATION);
    }


    // Return the amount of collateral to be drawn from a trove's collateral and sent as gas compensation.
    function _getCollGasCompensation(newColls memory _coll) internal pure returns (newColls memory) {
        require(_coll.tokens.length == _coll.amounts.length, "_getCollGasCompensation(): Collateral length mismatch");

        uint[] memory amounts = new uint[](_coll.tokens.length);
        for (uint i = 0; i < _coll.tokens.length; i++) {
            amounts[i] = _coll.amounts[i] / PERCENT_DIVISOR;
        }
        return newColls(_coll.tokens, amounts);
    }

    // Return the system's Total Virtual Coin Balance
    // Virtual Coins are a way to keep track of the system collateralization given
    // the collateral ratios of each collateral type
    function getEntireSystemColl() public view returns (uint entireSystemColl) {
        uint activeColl = activePool.getVC();
        uint liquidatedColl = defaultPool.getVC();

        return activeColl.add(liquidatedColl);
    }


    function getEntireSystemDebt() public override view returns (uint entireSystemDebt) {
        uint activeDebt = activePool.getYUSDDebt();
        uint closedDebt = defaultPool.getYUSDDebt();

        return activeDebt.add(closedDebt);
    }


    function _getICRColls(newColls memory _colls, uint _debt) internal view returns (uint ICR) {
        uint totalVC = _getVCColls(_colls);
        ICR = LiquityMath._computeCR(totalVC, _debt);
        return ICR;
    }


    function _getVC(address[] memory _tokens, uint[] memory _amounts) internal view returns (uint totalVC) {
        require(_tokens.length == _amounts.length, "Not same length");
        for (uint i = 0; i < _tokens.length; i++) {
            uint tokenVC = whitelist.getValueVC(_tokens[i], _amounts[i]);
            totalVC = totalVC.add(tokenVC);
        }
        return totalVC;
    }


    function _getVCColls(newColls memory _colls) internal view returns (uint VC) {
        for (uint i = 0; i < _colls.tokens.length; i++) {
            uint valueVC = whitelist.getValueVC(_colls.tokens[i], _colls.amounts[i]);
            VC = VC.add(valueVC);
        }
        return VC;
    }


    function _getUSDColls(newColls memory _colls) internal view returns (uint USDValue) {
        for (uint i = 0; i < _colls.tokens.length; i++) {
            uint valueUSD = whitelist.getValueUSD(_colls.tokens[i], _colls.amounts[i]);
            USDValue = USDValue.add(valueUSD);
        }
        return USDValue;
    }


    function _getTCR() internal view returns (uint TCR) {
        uint entireSystemColl = getEntireSystemColl();
        uint entireSystemDebt = getEntireSystemDebt();
        
        TCR = LiquityMath._computeCR(entireSystemColl, entireSystemDebt);
        return TCR;
    }


    function _checkRecoveryMode() internal view returns (bool) {
        uint TCR = _getTCR();

        return TCR < 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 exceeded provided maximum");
    }


    // get Colls struct for the given tokens and amounts
    function _getColls(address[] memory tokens, uint[] memory amounts) internal view returns (newColls memory coll) {
        require(tokens.length == amounts.length);
        coll.tokens = tokens;
        for (uint i = 0; i < tokens.length; i++) {
            coll.amounts[whitelist.getIndex(tokens[i])] = amounts[i];
        }
        return coll;
    }


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


    function _sendColl(address _to, newColls memory _coll) internal returns (bool) {
        for (uint i = 0; i < _coll.tokens.length; i++) {
            IERC20 token = IERC20(_coll.tokens[i]);
            if (!token.transfer(_to, _coll.amounts[i])) {
                return false;
            }
        }
        return true;
    }


    // Check whether or not the system *would be* in Recovery Mode, given the entire system coll and debt.
    // returns true if the system would be in recovery mode and false if not
    function _checkPotentialRecoveryMode(uint _entireSystemColl, uint _entireSystemDebt)
    internal
    pure
    returns (bool)
    {
        uint TCR = LiquityMath._computeCR(_entireSystemColl, _entireSystemDebt);

        return TCR < CCR;
    }



}

File 16 of 28 : Ownable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

/**
 * Based on OpenZeppelin's Ownable contract:
 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
 *
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == _owner;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     *
     * NOTE: This function is not safe, as it doesn’t check owner is calling it.
     * Make sure you check it before calling it.
     */
    function _renounceOwnership() internal {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }
}

File 17 of 28 : CheckContract.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;


contract CheckContract {
    /**
     * Check that the account is an already deployed non-destroyed contract.
     * See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L12
     */
    function checkContract(address _account) internal view {
        require(_account != address(0), "Account cannot be zero address");

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(_account) }
        require(size > 0, "Account code size cannot be zero");
    }
}

File 18 of 28 : ILiquityBase.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./IPriceFeed.sol";


interface ILiquityBase {

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

File 19 of 28 : IDefaultPool.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./IPool.sol";

interface IDefaultPool is IPool {
    // --- Events ---
    event TroveManagerAddressChanged(address _newTroveManagerAddress);
    event DefaultPoolYUSDDebtUpdated(uint _YUSDDebt);
    event DefaultPoolETHBalanceUpdated(uint _ETH);

    // --- Functions ---
    
    function sendCollsToActivePool(address[] memory _collaterals, uint[] memory _amounts, address _borrower) external;
    function addCollateralType(address _collateral) external;
    function getCollateralVC(address collateralAddress) external view returns (uint);
}

File 20 of 28 : IPriceFeed.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

interface IPriceFeed {

    // --- Events ---
    event LastGoodPriceUpdated(uint _lastGoodPrice);

    // --- Function ---
    // function fetchPrice() external returns (uint);

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

File 21 of 28 : ICollateralReceiver.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

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

File 22 of 28 : IERC20.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

/**
 * 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 23 of 28 : 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 24 of 28 : 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 ActivePoolAddressChanged(address _newActivePoolAddress);
    event DefaultPoolAddressChanged(address _newDefaultPoolAddress);
    event StabilityPoolAddressChanged(address _newStabilityPoolAddress);
    event WhitelistAddressChanged(address _newWhitelistAddress);
    event EtherSent(address _to, uint _amount);
    event CollateralSent(address _collateral, address _to, uint _amount);

    // --- Functions ---

    function getVC() external view returns (uint);

    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 25 of 28 : YetiCustomBase.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./BaseMath.sol";
import "./SafeMath.sol";
import "../Interfaces/IERC20.sol";
import "../Interfaces/IWhitelist.sol";


contract YetiCustomBase is BaseMath {
    using SafeMath for uint256;

    IWhitelist whitelist;

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

    // Collateral math

    // gets the sum of _coll1 and _coll2
    function _sumColls(newColls memory _coll1, newColls memory _coll2)
        internal
        view
        returns (newColls memory finalColls)
    {
        newColls memory coll3;

        coll3.tokens = whitelist.getValidCollateral();
        coll3.amounts = new uint256[](coll3.tokens.length);

        uint256 n = 0;
        for (uint256 i = 0; i < _coll1.tokens.length; i++) {
            uint256 tokenIndex = whitelist.getIndex(_coll1.tokens[i]);
            if (_coll1.amounts[i] > 0) {
                n++;
                coll3.amounts[tokenIndex] = _coll1.amounts[i];
            }
        }

        for (uint256 i = 0; i < _coll2.tokens.length; i++) {
            uint256 tokenIndex = whitelist.getIndex(_coll2.tokens[i]);
            if (_coll2.amounts[i] > 0) {
                if (coll3.amounts[tokenIndex] == 0) {
                    n++;
                }
                coll3.amounts[tokenIndex] = coll3.amounts[tokenIndex].add(_coll2.amounts[i]);
            }
        }

        address[] memory sumTokens = new address[](n);
        uint256[] memory sumAmounts = new uint256[](n);
        uint256 j = 0;

        // should only find n amounts over 0
        for (uint256 i = 0; i < coll3.tokens.length; i++) {
            if (coll3.amounts[i] > 0) {
                sumTokens[j] = coll3.tokens[i];
                sumAmounts[j] = coll3.amounts[i];
                j++;
            }
        }
        finalColls.tokens = sumTokens;
        finalColls.amounts = sumAmounts;
    }


    // gets the sum of coll1 with tokens and amounts
    function _sumColls(
        newColls memory _coll1,
        address[] memory tokens,
        uint256[] memory amounts
    ) internal view returns (newColls memory) {
        newColls memory coll2 = newColls(tokens, amounts);
        return _sumColls(_coll1, coll2);
    }


    function _sumColls(
        address[] memory tokens1,
        uint256[] memory amounts1,
        address[] memory tokens2,
        uint256[] memory amounts2
    ) internal view returns (newColls memory) {
        newColls memory coll1 = newColls(tokens1, amounts1);
        return _sumColls(coll1, tokens2, amounts2);
    }


    // Function for summing colls when coll1 includes all the tokens in the whitelist
    // Used in active, default, stability, and surplus pools
    // assumes _coll1.tokens = all whitelisted tokens
    function _leftSumColls(
        newColls memory _coll1,
        address[] memory _tokens,
        uint256[] memory _amounts
    ) internal view returns (uint[] memory) {
        uint[] memory sumAmounts = _getArrayCopy(_coll1.amounts);

        // assumes that sumAmounts length = whitelist tokens length.
        for (uint256 i = 0; i < _tokens.length; i++) {
            uint tokenIndex = whitelist.getIndex(_tokens[i]);
            sumAmounts[tokenIndex] = sumAmounts[tokenIndex].add(_amounts[i]);
        }

        return sumAmounts;
    }


    // Function for summing colls when one list is all tokens. Used in active, default, stability, and surplus pools
    function _leftSubColls(newColls memory _coll1, address[] memory _subTokens, uint[] memory _subAmounts)
        internal
        view
        returns (uint[] memory)
    {
        uint[] memory diffAmounts = _getArrayCopy(_coll1.amounts);

        //assumes that coll1.tokens = whitelist tokens. Keeps all of coll1's tokens, and subtracts coll2's amounts
        for (uint256 i = 0; i < _subTokens.length; i++) {
            uint256 tokenIndex = whitelist.getIndex(_subTokens[i]);
            diffAmounts[tokenIndex] = diffAmounts[tokenIndex].sub(_subAmounts[i]);
        }
        return diffAmounts;
    }
    

    // Returns _coll1 minus _tokens and _amounts
    // will error if _tokens include a token not in _coll1.tokens
    function _subColls(newColls memory _coll1, address[] memory _tokens, uint[] memory _amounts)
        internal
        view
        returns (newColls memory finalColls)
    {
        require(_tokens.length == _amounts.length, "Sub Colls invalid input");

        newColls memory coll3;
        coll3.tokens = whitelist.getValidCollateral();
        coll3.amounts = new uint256[](coll3.tokens.length);
        uint256 n = 0;

        for (uint256 i = 0; i < _coll1.tokens.length; i++) {
            if (_coll1.amounts[i] > 0) {
                uint256 tokenIndex = whitelist.getIndex(_coll1.tokens[i]);
                coll3.amounts[tokenIndex] = _coll1.amounts[i];
                n++;
            }
        }

        for (uint256 i = 0; i < _tokens.length; i++) {
            uint256 tokenIndex = whitelist.getIndex(_tokens[i]);
            require(coll3.amounts[tokenIndex] >= _amounts[i], "illegal sub");
            coll3.amounts[tokenIndex] = coll3.amounts[tokenIndex].sub(_amounts[i]);
            if (coll3.amounts[tokenIndex] == 0) {
                n--;
            }
        }

        address[] memory diffTokens = new address[](n);
        uint256[] memory diffAmounts = new uint256[](n);
        uint256 j = 0;

        for (uint256 i = 0; i < coll3.tokens.length; i++) {
            if (coll3.amounts[i] > 0) {
                diffTokens[j] = coll3.tokens[i];
                diffAmounts[j] = coll3.amounts[i];
                j++;
            }
        }
        finalColls.tokens = diffTokens;
        finalColls.amounts = diffAmounts;
    }

    function _getArrayCopy(uint[] memory _arr) internal pure returns (uint[] memory){
        uint[] memory copy = new uint[](_arr.length);
        for (uint i = 0; i < _arr.length; i++) {
            copy[i] = _arr[i];
        }
        return copy;
    }
}

File 26 of 28 : BaseMath.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.6.11;


contract BaseMath {
    uint constant public DECIMAL_PRECISION = 1e18;
}

File 27 of 28 : SafeMath.sol
// SPDX-License-Identifier: UNLICENSED

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, "SafeMath: addition 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, "SafeMath: subtraction 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, "SafeMath: multiplication 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, "SafeMath: division by zero");
    }

    /**
     * @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, "SafeMath: modulo by zero");
    }

    /**
     * @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 28 of 28 : LiquityMath.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.11;

import "./SafeMath.sol";

library LiquityMath {
    using SafeMath for uint;

    uint internal constant DECIMAL_PRECISION = 1e18;

    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;
    }

    /* 
    * Multiply two decimal numbers and use normal rounding rules:
    * -round product up if 19'th mantissa digit >= 5
    * -round product down if 19'th mantissa digit < 5
    *
    * Used only inside the exponentiation, _decPow().
    */
    function decMul(uint x, uint y) internal pure returns (uint decProd) {
        uint prod_xy = x.mul(y);

        decProd = prod_xy.add(DECIMAL_PRECISION / 2).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 > 525600000) {_minutes = 525600000;}  // 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);
    }

    //  _coll should be the amount of VC and _debt is debt of YUSD\
    // new collateral ratio is 10**18 times the collateral ratio. (150% => 1.5e18)
    function _computeCR(uint _coll, uint _debt) internal pure returns (uint) {
        if (_debt > 0) {
            uint newCollRatio = _coll.mul(10**18).div(_debt);
            return newCollRatio;
        }
        // Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR.
        else { // if (_debt == 0)
            return 2**256 - 1; 
        }
    }

}

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

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_activePoolAddress","type":"address"}],"name":"ActivePoolAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newBorrowerOperationsAddress","type":"address"}],"name":"BorrowerOperationsAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_collSurplusPoolAddress","type":"address"}],"name":"CollSurplusPoolAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_defaultPoolAddress","type":"address"}],"name":"DefaultPoolAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_gasPoolAddress","type":"address"}],"name":"GasPoolAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","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":"address","name":"_sYETIAddress","type":"address"}],"name":"SYETIAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sortedTrovesAddress","type":"address"}],"name":"SortedTrovesAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_stabilityPoolAddress","type":"address"}],"name":"StabilityPoolAddressChanged","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_yetiTokenAddress","type":"address"}],"name":"YETITokenAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newYUSDTokenAddress","type":"address"}],"name":"YUSDTokenAddressChanged","type":"event"},{"inputs":[],"name":"BETA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BOOTSTRAP_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BORROWING_FEE_FLOOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DECIMAL_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_NET_DEBT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DIVISOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REDEMPTION_FEE_FLOOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"YUSD_GAS_COMPENSATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_100pct","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_110pct","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activePool","outputs":[{"internalType":"contract IActivePool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerOperationsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultPool","outputs":[{"internalType":"contract IDefaultPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemColl","outputs":[{"internalType":"uint256","name":"entireSystemColl","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemDebt","outputs":[{"internalType":"uint256","name":"entireSystemDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":"_partialRedemptionHintICR","type":"uint256"},{"internalType":"uint256","name":"_maxIterations","type":"uint256"},{"internalType":"address","name":"_redeemer","type":"address"}],"name":"redeemCollateral","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":"_partialRedemptionHintICR","type":"uint256"},{"internalType":"address","name":"_collToRedeem","type":"address"}],"name":"redeemCollateralSingle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrowerOperationsAddress","type":"address"},{"internalType":"address","name":"_activePoolAddress","type":"address"},{"internalType":"address","name":"_defaultPoolAddress","type":"address"},{"internalType":"address","name":"_stabilityPoolAddress","type":"address"},{"internalType":"address","name":"_gasPoolAddress","type":"address"},{"internalType":"address","name":"_collSurplusPoolAddress","type":"address"},{"internalType":"address","name":"_yusdTokenAddress","type":"address"},{"internalType":"address","name":"_sortedTrovesAddress","type":"address"},{"internalType":"address","name":"_yetiTokenAddress","type":"address"},{"internalType":"address","name":"_sYETIAddress","type":"address"},{"internalType":"address","name":"_whitelistAddress","type":"address"},{"internalType":"address","name":"_troveManagerAddress","type":"address"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sortedTroves","outputs":[{"internalType":"contract ISortedTroves","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"troveManager","outputs":[{"internalType":"contract ITroveManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"troveManagerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"troveManagerLiquidationsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"troveManagerRedemptionsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50600380546001600160a01b031916339081179091556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3614b35806100606000396000f3fe608060405234801561001057600080fd5b50600436106101795760003560e01c8063734f622d116100d9578063a20baee611610087578063a20baee614610258578063ae9187541461031f578063b7f8cf9b14610327578063c2f6202d1461032f578063c35bc55014610386578063f5f468ed1461038e578063f92d3433146101c457610179565b8063734f622d14610260578063794e5724146101cc578063795d26c3146102e35780637f7dde4a146102eb578063887105d3146102f35780638da5cb5b146102fb5780638f32d59b1461030357610179565b80633d83908a116101365780633d83908a146101dc57806343338372146101e45780634870dd9a14610238578063499f99d9146102405780635733d58f146102485780635a4d28bb1461025057806372fe25aa1461025857610179565b8063071a75411461017e57806316a81f31146101985780631bf43555146101bc57806328d28b5b146101c4578063308e9647146101cc5780633cc74225146101d4575b600080fd5b610186610396565b60408051918252519081900360200190f35b6101a061039b565b604080516001600160a01b039092168252519081900360200190f35b6101866103aa565b6101866103b7565b6101866103c2565b6101a06103ce565b6101a06103dd565b610236600480360360e08110156101fa57600080fd5b508035906020810135906001600160a01b0360408201358116916060810135821691608082013581169160a08101359160c090910135166103ec565b005b6101866116c1565b6101866116c6565b6101866116d3565b6101a06116df565b6101866116ee565b610236600480360361018081101561027757600080fd5b506001600160a01b038135811691602081013582169160408201358116916060810135821691608082013581169160a081013582169160c082013581169160e08101358216916101008201358116916101208101358216916101408201358116916101600135166116fa565b610186611acd565b6101a0611bda565b610186611be9565b6101a0611cb1565b61030b611cc0565b604080519115158252519081900360200190f35b6101a0611cd1565b6101a0611ce0565b610236600480360361010081101561034657600080fd5b508035906020810135906001600160a01b0360408201358116916060810135821691608082013581169160a08101359160c08201359160e0013516611cef565b61018661260d565b6101a0612614565b600281565b600f546001600160a01b031681565b686194049f30f720000081565b6611c37937e0800081565b670f43fc2c04ee000081565b6002546001600160a01b031681565b6006546001600160a01b031681565b6103f4614858565b506040805160e0810182526001546001600160a01b039081168252600254811660208301526007548116928201929092526009548216606082015260105482166080820152601154821660a0820152600c5490911660c0820152610456614894565b61045e6148d7565b6001600160a01b0380891660408301526060820186905287811682528616602082015261048b8a8a612623565b610493612701565b61049b6127c8565b6104a48a612818565b6104ac611acd565b60a0830181905260408085015181516370a0823160e01b815233600482015291516001600160a01b03909116916370a08231916024808301926020929190829003018186803b1580156104fe57600080fd5b505afa158015610512573d6000803e3d6000fd5b505050506040513d602081101561052857600080fd5b5051111561053257fe5b898252608083015160408201516105499190612857565b61059a576040805162461bcd60e51b815260206004820152601d60248201527f496e76616c696420666972737420726564656d7074696f6e2068696e74000000604482015290519081900360640190fd5b60065460408083015181516327fb7d8960e01b81526001600160a01b0391821660048201529151670f43fc2c04ee00009391909116916327fb7d89916024808301926020929190829003018186803b1580156105f557600080fd5b505afa158015610609573d6000803e3d6000fd5b505050506040513d602081101561061f57600080fd5b5051101561066a576040805162461bcd60e51b81526020600482015260136024820152722a3937bb329034b9903ab73232b93bb0ba32b960691b604482015290519081900360640190fd5b6006546040808301518151630b07655760e01b81526001600160a01b0391821660048201529151921691630b0765579160248082019260009290919082900301818387803b1580156106bb57600080fd5b505af11580156106cf573d6000803e3d6000fd5b505050506106db6148fe565b600654604080840151815163d66a255360e01b81526001600160a01b0391821660048201529151600093919091169163d66a2553916024808301926020929190829003018186803b15801561072f57600080fd5b505afa158015610743573d6000803e3d6000fd5b505050506040513d602081101561075957600080fd5b505184519091506107829061077d83680ad78ebc5ac620000063ffffffff612aae16565b612af7565b825261078c614925565b6006546040808601518151630d68162d60e21b81526001600160a01b03918216600482015291519216916335a058b491602480820192600092909190829003018186803b1580156107dc57600080fd5b505afa1580156107f0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052606081101561081957600080fd5b8101908080516040519392919084600160201b82111561083857600080fd5b90830190602082018581111561084d57600080fd5b82518660208202830111600160201b8211171561086957600080fd5b82525081516020918201928201910280838360005b8381101561089657818101518382015260200161087e565b5050505090500160405260200180516040519392919084600160201b8211156108be57600080fd5b9083019060208201858111156108d357600080fd5b82518660208202830111600160201b821117156108ef57600080fd5b82525081516020918201928201910280838360005b8381101561091c578181015183820152602001610904565b505050509190910160405250505050602083015281526000805b82515182101561098457886001600160a01b03168360000151838151811061095a57fe5b60200260200101516001600160a01b0316141561097957506001610984565b600190910190610936565b806109c05760405162461bcd60e51b815260040180806020018281038252602781526020018061498e6027913960400191505060405180910390fd5b5060008054602084015180516001600160a01b039092169163cacabd27918c91869081106109ea57fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b031681526020018281526020019250505060206040518083038186803b158015610a3f57600080fd5b505afa158015610a53573d6000803e3d6000fd5b505050506040513d6020811015610a6957600080fd5b50518551909150610a7b908290612af7565b8552825160208601515282515167ffffffffffffffff81118015610a9e57600080fd5b50604051908082528060200260200182016040528015610ac8578160200160208202803683370190505b508560200151602001819052506000610b1582610b0986602001518681518110610aee57fe5b60200260200101518960000151612b0d90919063ffffffff16565b9063ffffffff612b6616565b9050610b418185602001518581518110610b2b57fe5b6020026020010151612aae90919063ffffffff16565b84602001518481518110610b5157fe5b602002602001018181525050808660200151602001518481518110610b7257fe5b6020908102919091010152600054845180516001600160a01b039092169163495ee13e919086908110610ba157fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015610bef57600080fd5b505afa158015610c03573d6000803e3d6000fd5b505050506040513d6020811015610c1957600080fd5b505115610cad578351805184908110610c2e57fe5b6020908102919091010151604080890151815163164746fd60e11b81526001600160a01b039182166004820152336024820152604481018590529151921691632c8e8dfa9160648082019260009290919082900301818387803b158015610c9457600080fd5b505af1158015610ca8573d6000803e3d6000fd5b505050505b50508351600090610cc590859063ffffffff612aae16565b9050680ad78ebc5ac6200000811415610eb85760065460408088015181516362a861b160e11b81526001600160a01b039182166004820152915192169163c550c3629160248082019260009290919082900301818387803b158015610d2957600080fd5b505af1158015610d3d573d6000803e3d6000fd5b50506006546040808a0151815163bd5ef3f960e01b81526001600160a01b03918216600482015291519216935063bd5ef3f9925060248082019260009290919082900301818387803b158015610d9257600080fd5b505af1158015610da6573d6000803e3d6000fd5b50505050610dcc888760400151680ad78ebc5ac620000086600001518760200151612ba8565b60408051600080825260208201818152828401808552938a0151828552929390926001600160a01b031691600080516020614a168339815191529185908590600390606084016080850160a086018460ff168152602001838103835286818151815260200191508051906020019060200280838360005b83811015610e5b578181015183820152602001610e43565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015610e9a578181015183820152602001610e82565b50505050905001965050505050505060405180910390a250506112b3565b6000610ed5610ecf85600001518660200151612e98565b83612fcb565b6060880151909150610ef49066470de4df82000063ffffffff61300316565b81101580610f1d57506060870151610f199066470de4df82000063ffffffff612aae16565b8111155b80610f385750686194049f30f7200000610f368361305d565b105b15610f745760405162461bcd60e51b815260040180806020018281038252603c815260200180614ac4603c913960400191505060405180910390fd5b6080890151604080890151895160208b0151835163015f109360e51b81526001600160a01b039384166004820152602481018790529183166044830152821660648201529151921691632be212609160848082019260009290919082900301818387803b158015610fe457600080fd5b505af1158015610ff8573d6000803e3d6000fd5b50506006546040808b015181516331bafd9b60e01b81526001600160a01b039182166004820152602481018890529151921693506331bafd9b925060448082019260009290919082900301818387803b15801561105457600080fd5b505af1158015611068573d6000803e3d6000fd5b50506006546040808b015188516020808b0151935163030877f160e31b81526001600160a01b0384811660048301908152606060248401908152855160648501528551929098169950631843bf88985094969395949392604483019260840191818801910280838360005b838110156110eb5781810151838201526020016110d3565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561112a578181015183820152602001611112565b5050505090500195505050505050600060405180830381600087803b15801561115257600080fd5b505af1158015611166573d6000803e3d6000fd5b50506006546040808b01518151630c7940bd60e11b81526001600160a01b0391821660048201529151921693506318f2817a925060248082019260009290919082900301818387803b1580156111bb57600080fd5b505af11580156111cf573d6000803e3d6000fd5b5050505086604001516001600160a01b0316600080516020614a168339815191528386600001518760200151600360405180858152602001806020018060200184600381111561121b57fe5b60ff168152602001838103835286818151815260200191508051906020019060200280838360005b8381101561125b578181015183820152602001611243565b50505050905001838103825285818151815260200191508051906020019060200280838360005b8381101561129a578181015183820152602001611282565b50505050905001965050505050505060405180910390a2505b8451602080890191909152850151604088018190526112d190613080565b6112da57600080fd5b6112ec87602001518860a001516130cc565b506112fa8760200151613214565b606088018190526040890151885161132292339161131d9163ffffffff61300316565b613299565b61133087606001518f613359565b6040808901516060808b0151908a015183516323b872dd60e01b81523360048201526001600160a01b039283166024820152604481019190915292519116916323b872dd9160648083019260209291908290030181600087803b15801561139657600080fd5b505af11580156113aa573d6000803e3d6000fd5b505050506040513d60208110156113c057600080fd5b505086516020888101516060808b01516040808d0151805190860151825188815280880187905292830184905260a094830185815282519584019590955281517f7335fe3ff0d234203ba5c8aac0cb19ed9bc9651b015ffcbcca39e73531b32e8d98979495929491939291608084019160c0850191878101910280838360005b83811015611458578181015183820152602001611440565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561149757818101518382015260200161147f565b5050505090500197505050505050505060405180910390a187604001516001600160a01b0316639dc29fac3389602001516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561151757600080fd5b505af115801561152b573d6000803e3d6000fd5b5050505087600001516001600160a01b031663e7b1d67888602001516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561157d57600080fd5b505af1158015611591573d6000803e3d6000fd5b505089516040808b01518051602091820151925163ab471ec560e01b815233600482018181526000606484018190526080602485019081528551608486015285516001600160a01b039099169a5063ab471ec59950929794969590949193604481019260a4909101918089019102808383895b8381101561161c578181015183820152602001611604565b50505050905001838103825285818151815260200191508051906020019060200280838360005b8381101561165b578181015183820152602001611643565b505050509050019650505050505050602060405180830381600087803b15801561168457600080fd5b505af1158015611698573d6000803e3d6000fd5b505050506040513d60208110156116ae57600080fd5b5050505050505050505050505050505050565b60c881565b680ad78ebc5ac620000081565b6714d1120d7b16000081565b600d546001600160a01b031681565b670de0b6b3a764000081565b611702611cc0565b611753576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b61175c8c6133a9565b6117658b6133a9565b61176e8a6133a9565b611777896133a9565b611780886133a9565b611789876133a9565b611792866133a9565b61179b856133a9565b6117a4846133a9565b6117ad836133a9565b6117b6826133a9565b6117bf816133a9565b600480546001600160a01b03199081166001600160a01b038f81169182179093556001805483168f85161790556002805483168e85161790556005805483168d8516179055600080548316868516179055600c805483168c85161790556011805483168b85161790556007805483168a85161790556010805483168985161790556008805483168885161790556009805483168785161790556006805490921692841692909217905560408051918252517f3ca631ffcd2a9b5d9ae18543fc82f58eb4ca33af9e6ab01b7a8e95331e6ed985916020908290030190a1604080516001600160a01b038d16815290517f78f058b189175430c48dc02699e3a0031ea4ff781536dc2fab847de4babdd8829181900360200190a1604080516001600160a01b038c16815290517f5ee0cae2f063ed938bb55046f6a932fb6ae792bf43624806bb90abe68a50be9b9181900360200190a1604080516001600160a01b038b16815290517f82966d27eea39b038ee0fa30cd16532bb24f6e65d31cb58fb227aa5766cdcc7f9181900360200190a1604080516001600160a01b038a16815290517fcfb07d791fcafc032b35837b50eb84b74df518cf4cc287e8084f47630fa70fa09181900360200190a1604080516001600160a01b038916815290517fe67f36a6e961157d6eff83b91f3af5a62131ceb6f04954ef74f51c1c05e7f88d9181900360200190a1604080516001600160a01b038816815290517fc3cd2681391697b6aa2901e9d80dd9dd3218bb54e24e0817c566fa568adeba369181900360200190a1604080516001600160a01b038716815290517f65f4cf077bc01e4742eb5ad98326f6e95b63548ea24b17f8d5e823111fe788009181900360200190a1604080516001600160a01b038616815290517f449ef232e1f38887f74a15dce9059e717b689423d5270f46e1d478e4bbd1c91e9181900360200190a1604080516001600160a01b038516815290517f2ea605b72d3979601397bc2b968c2390809927edefe7ec75779d5325fb0ba0799181900360200190a1611abf613458565b505050505050505050505050565b600080600160009054906101000a90046001600160a01b03166001600160a01b0316638df709926040518163ffffffff1660e01b815260040160206040518083038186803b158015611b1e57600080fd5b505afa158015611b32573d6000803e3d6000fd5b505050506040513d6020811015611b4857600080fd5b5051600254604080516346fb84c960e11b815290519293506000926001600160a01b0390921691638df7099291600480820192602092909190829003018186803b158015611b9557600080fd5b505afa158015611ba9573d6000803e3d6000fd5b505050506040513d6020811015611bbf57600080fd5b50519050611bd3828263ffffffff61300316565b9250505090565b6001546001600160a01b031681565b600080600160009054906101000a90046001600160a01b03166001600160a01b03166301d40b636040518163ffffffff1660e01b815260040160206040518083038186803b158015611c3a57600080fd5b505afa158015611c4e573d6000803e3d6000fd5b505050506040513d6020811015611c6457600080fd5b5051600254604080516301d40b6360e01b815290519293506000926001600160a01b03909216916301d40b6391600480820192602092909190829003018186803b158015611b9557600080fd5b6003546001600160a01b031690565b6003546001600160a01b0316331490565b6010546001600160a01b031681565b6004546001600160a01b031681565b611cf76134a2565b611cff614858565b506040805160e0810182526001546001600160a01b039081168252600254811660208301526007548116928201929092526009548216606082015260105482166080820152601154821660a0820152600c5490911660c0820152611d61614894565b611d6b8a8a612623565b611d73612701565b611d7b6127c8565b611d848a612818565b611d8c611acd565b60a0820181905260408084015181516370a0823160e01b81526001600160a01b038781166004830152925192909116916370a0823191602480820192602092909190829003018186803b158015611de257600080fd5b505afa158015611df6573d6000803e3d6000fd5b505050506040513d6020811015611e0c57600080fd5b50511115611e1657fe5b8981526080820151600090611e2b908a612857565b15611e37575087611fce565b82608001516001600160a01b0316634d6228316040518163ffffffff1660e01b815260040160206040518083038186803b158015611e7457600080fd5b505afa158015611e88573d6000803e3d6000fd5b505050506040513d6020811015611e9e57600080fd5b505190505b6001600160a01b03811615801590611f3c5750600654604080516327fb7d8960e01b81526001600160a01b0384811660048301529151670f43fc2c04ee00009392909216916327fb7d8991602480820192602092909190829003018186803b158015611f0e57600080fd5b505afa158015611f22573d6000803e3d6000fd5b505050506040513d6020811015611f3857600080fd5b5051105b15611fce5782608001516001600160a01b031663b72703ac826040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015611f9b57600080fd5b505afa158015611faf573d6000803e3d6000fd5b505050506040513d6020811015611fc557600080fd5b50519050611ea3565b84611fd95760001994505b6001600160a01b03811615801590611ff15750815115155b8015611ffd5750600085115b1561220657848060019003955050600083608001516001600160a01b031663b72703ac836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561206757600080fd5b505afa15801561207b573d6000803e3d6000fd5b505050506040513d602081101561209157600080fd5b5051600654604080516327fb7d8960e01b81526001600160a01b0386811660048301529151939450670f43fc2c04ee00009391909216916327fb7d89916024808301926020929190829003018186803b1580156120ed57600080fd5b505afa158015612101573d6000803e3d6000fd5b505050506040513d602081101561211757600080fd5b5051106121ff5760065460408051630b07655760e01b81526001600160a01b03858116600483015291519190921691630b07655791602480830192600092919082900301818387803b15801561216c57600080fd5b505af1158015612180573d6000803e3d6000fd5b5050505061218c6148fe565b61219f85878587600001518e8e8e6134b9565b90508060400151156121b2575050612206565b805160208501516121c89163ffffffff61300316565b8460200181815250506121e3846040015182602001516140cd565b6040850152805184516121fb9163ffffffff612aae16565b8452505b9050611fd9565b6122138260400151613080565b61221c57600080fd5b61222e82602001518360a001516130cc565b5061223c8260200151613214565b60608301819052604084015161225f91869061131d908f9063ffffffff61300316565b61226d82606001518b613359565b6040808401516060808601519085015183516323b872dd60e01b81526001600160a01b0389811660048301529283166024820152604481019190915292519116916323b872dd9160648083019260209291908290030181600087803b1580156122d557600080fd5b505af11580156122e9573d6000803e3d6000fd5b505050506040513d60208110156122ff57600080fd5b8101908080519060200190929190505050507f7335fe3ff0d234203ba5c8aac0cb19ed9bc9651b015ffcbcca39e73531b32e8d8b83602001518460600151856040015160000151866040015160200151604051808681526020018581526020018481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156123a557818101518382015260200161238d565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156123e45781810151838201526020016123cc565b5050505090500197505050505050505060405180910390a182604001516001600160a01b0316639dc29fac8584602001516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561246457600080fd5b505af1158015612478573d6000803e3d6000fd5b5050505082600001516001600160a01b031663e7b1d67883602001516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156124ca57600080fd5b505af11580156124de573d6000803e3d6000fd5b505084516040808601518051602091820151925163ab471ec560e01b81526001600160a01b038b81166004830190815260006064840181905260806024850190815285516084860152855193909816995063ab471ec598508d97949695909491939192604483019260a401918089019102808383895b8381101561256c578181015183820152602001612554565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156125ab578181015183820152602001612593565b505050509050019650505050505050602060405180830381600087803b1580156125d457600080fd5b505af11580156125e8573d6000803e3d6000fd5b505050506040513d60208110156125fe57600080fd5b50505050505050505050505050565b6212750081565b600e546001600160a01b031681565b600061264183610b0984670de0b6b3a764000063ffffffff612b0d16565b90506611c37937e0800081101561269f576040805162461bcd60e51b815260206004820152601d60248201527f4d617820666565206d757374206265206174206c6561737420302e3525000000604482015290519081900360640190fd5b670de0b6b3a76400008111156126fc576040805162461bcd60e51b815260206004820152601c60248201527f4d617820666565206d757374206265206174206d6f7374203130302500000000604482015290519081900360640190fd5b505050565b60085460408051631e425be160e11b815290516000926001600160a01b031691633c84b7c2916004808301926020929190829003018186803b15801561274657600080fd5b505afa15801561275a573d6000803e3d6000fd5b505050506040513d602081101561277057600080fd5b50519050612787816212750063ffffffff61300316565b4210156127c55760405162461bcd60e51b81526004018080602001828103825260408152602001806149b56040913960400191505060405180910390fd5b50565b670f43fc2c04ee00006127d96145eb565b10156128165760405162461bcd60e51b815260040180806020018281038252602a815260200180614a9a602a913960400191505060405180910390fd5b565b600081116127c55760405162461bcd60e51b815260040180806020018281038252602e815260200180614a36602e913960400191505060405180910390fd5b60006001600160a01b03821615806128ee5750826001600160a01b0316635dbe47e8836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156128c057600080fd5b505afa1580156128d4573d6000803e3d6000fd5b505050506040513d60208110156128ea57600080fd5b5051155b8061297a5750600654604080516327fb7d8960e01b81526001600160a01b0385811660048301529151670f43fc2c04ee00009392909216916327fb7d8991602480820192602092909190829003018186803b15801561294c57600080fd5b505afa158015612960573d6000803e3d6000fd5b505050506040513d602081101561297657600080fd5b5051105b1561298757506000612aa8565b6000836001600160a01b031663765e0159846040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156129df57600080fd5b505afa1580156129f3573d6000803e3d6000fd5b505050506040513d6020811015612a0957600080fd5b505190506001600160a01b0381161580612aa45750600654604080516327fb7d8960e01b81526001600160a01b0384811660048301529151670f43fc2c04ee00009392909216916327fb7d8991602480820192602092909190829003018186803b158015612a7657600080fd5b505afa158015612a8a573d6000803e3d6000fd5b505050506040513d6020811015612aa057600080fd5b5051105b9150505b92915050565b6000612af083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061460e565b9392505050565b6000818310612b065781612af0565b5090919050565b600082612b1c57506000612aa8565b82820282848281612b2957fe5b0414612af05760405162461bcd60e51b81526004018080602001828103825260218152602001806149f56021913960400191505060405180910390fd5b6000612af083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506146a5565b604080860151600c548251632770a7eb60e21b81526001600160a01b039182166004820152602481018790529251911691639dc29fac91604480830192600092919082900301818387803b158015612bff57600080fd5b505af1158015612c13573d6000803e3d6000fd5b5050505084600001516001600160a01b031663e7b1d678846040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612c6157600080fd5b505af1158015612c75573d6000803e3d6000fd5b505050508460a001516001600160a01b0316639efc9e208584846040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b031681526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015612cfa578181015183820152602001612ce2565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015612d39578181015183820152602001612d21565b5050505090500195505050505050600060405180830381600087803b158015612d6157600080fd5b505af1158015612d75573d6000803e3d6000fd5b5050505084600001516001600160a01b031663d0d8c20d8660a0015184846040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b031681526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015612dfe578181015183820152602001612de6565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015612e3d578181015183820152602001612e25565b5050505090500195505050505050602060405180830381600087803b158015612e6557600080fd5b505af1158015612e79573d6000803e3d6000fd5b505050506040513d6020811015612e8f57600080fd5b50505050505050565b60008151835114612ee2576040805162461bcd60e51b815260206004820152600f60248201526e09cdee840e6c2daca40d8cadccee8d608b1b604482015290519081900360640190fd5b60005b8351811015612fc4576000805485516001600160a01b0390911690632e2b1a8890879085908110612f1257fe5b6020026020010151868581518110612f2657fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b031681526020018281526020019250505060206040518083038186803b158015612f7b57600080fd5b505afa158015612f8f573d6000803e3d6000fd5b505050506040513d6020811015612fa557600080fd5b50519050612fb9838263ffffffff61300316565b925050600101612ee5565b5092915050565b60008115612ffa576000612ff183610b0986670de0b6b3a764000063ffffffff612b0d16565b9150612aa89050565b50600019612aa8565b600082820183811015612af0576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600061307882680ad78ebc5ac620000063ffffffff612aae16565b90505b919050565b6000805b8260200151518110156130c3576000836020015182815181106130a357fe5b602002602001015111156130bb57600191505061307b565b600101613084565b50600092915050565b600080600660009054906101000a90046001600160a01b03166001600160a01b031663459799786040518163ffffffff1660e01b815260040160206040518083038186803b15801561311d57600080fd5b505afa158015613131573d6000803e3d6000fd5b505050506040513d602081101561314757600080fd5b50519050600061316984610b0987670de0b6b3a764000063ffffffff612b0d16565b9050600061318e61318183600263ffffffff612b6616565b849063ffffffff61300316565b90506131a281670de0b6b3a7640000612af7565b6006546040805163198141d560e11b81526004810184905290519293506001600160a01b039091169163330283aa9160248082019260009290919082900301818387803b1580156131f257600080fd5b505af1158015613206573d6000803e3d6000fd5b509298975050505050505050565b6000613078600660009054906101000a90046001600160a01b03166001600160a01b0316632b11551a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561326757600080fd5b505afa15801561327b573d6000803e3d6000fd5b505050506040513d602081101561329157600080fd5b50518361470a565b80836001600160a01b03166370a08231846040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156132f057600080fd5b505afa158015613304573d6000803e3d6000fd5b505050506040513d602081101561331a57600080fd5b505110156126fc5760405162461bcd60e51b815260040180806020018281038252604e815260200180614940604e913960600191505060405180910390fd5b808211156133a5576040805162461bcd60e51b815260206004820152601460248201527355736572206d757374206163636570742066656560601b604482015290519081900360640190fd5b5050565b6001600160a01b038116613404576040805162461bcd60e51b815260206004820152601e60248201527f4163636f756e742063616e6e6f74206265207a65726f20616464726573730000604482015290519081900360640190fd5b803b806133a5576040805162461bcd60e51b815260206004820181905260248201527f4163636f756e7420636f64652073697a652063616e6e6f74206265207a65726f604482015290519081900360640190fd5b6003546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600380546001600160a01b0319169055565b6006546001600160a01b0316331461281657600080fd5b6134c16148fe565b6006546040805163d66a255360e01b81526001600160a01b038981166004830152915161355b93899361077d93680ad78ebc5ac620000093919092169163d66a2553916024808301926020929190829003018186803b15801561352357600080fd5b505afa158015613537573d6000803e3d6000fd5b505050506040513d602081101561354d57600080fd5b50519063ffffffff612aae16565b8152613565614925565b60065460408051630d68162d60e21b81526001600160a01b038a81166004830152915191909216916335a058b4916024808301926000929190829003018186803b1580156135b257600080fd5b505afa1580156135c6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260608110156135ef57600080fd5b8101908080516040519392919084600160201b82111561360e57600080fd5b90830190602082018581111561362357600080fd5b82518660208202830111600160201b8211171561363f57600080fd5b82525081516020918201928201910280838360005b8381101561366c578181015183820152602001613654565b5050505090500160405260200180516040519392919084600160201b82111561369457600080fd5b9083019060208201858111156136a957600080fd5b82518660208202830111600160201b821117156136c557600080fd5b82525081516020918201928201910280838360005b838110156136f25781810151838201526020016136da565b50505050919091016040525050505060208301528082525160609067ffffffffffffffff8111801561372357600080fd5b5060405190808252806020026020018201604052801561374d578160200160208202803683370190505b509050600061375b83614769565b845190915060009061377b90670de0b6b3a764000063ffffffff612b0d16565b845160208701515284515190915067ffffffffffffffff8111801561379f57600080fd5b506040519080825280602002602001820160405280156137c9578160200160208202803683370190505b50602080870151015260005b8451518110156139bd57600061381b670de0b6b3a7640000610b0986610b098a60200151878151811061380457fe5b602002602001015188612b0d90919063ffffffff16565b90506138318187602001518481518110610b2b57fe5b85838151811061383d57fe5b60200260200101818152505080876020015160200151838151811061385e57fe5b6020908102919091010152600054865180516001600160a01b039092169163495ee13e91908590811061388d57fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156138db57600080fd5b505afa1580156138ef573d6000803e3d6000fd5b505050506040513d602081101561390557600080fd5b5051156139b457855180518390811061391a57fe5b60200260200101516001600160a01b0316632c8e8dfa8d8f846040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160a01b03166001600160a01b031681526020018281526020019350505050600060405180830381600087803b15801561399b57600080fd5b505af11580156139af573d6000803e3d6000fd5b505050505b506001016137d5565b5084516006546040805163d66a255360e01b81526001600160a01b038e811660048301529151600094613a13949093169163d66a2553916024808301926020929190829003018186803b15801561352357600080fd5b90506000613a25866000015186612e98565b9050680ad78ebc5ac6200000821415613c4557600654604080516362a861b160e11b81526001600160a01b038f811660048301529151919092169163c550c36291602480830192600092919082900301818387803b158015613a8657600080fd5b505af1158015613a9a573d6000803e3d6000fd5b50505050600660009054906101000a90046001600160a01b03166001600160a01b031663bd5ef3f98d6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b03168152602001915050600060405180830381600087803b158015613b0b57600080fd5b505af1158015613b1f573d6000803e3d6000fd5b50505050613b3d8e8d680ad78ebc5ac6200000896000015189612ba8565b604080516000808252602082019092526060915060408051600080825260208201909252919250606091905090508d6001600160a01b0316600080516020614a16833981519152600084846003604051808581526020018060200180602001846003811115613ba857fe5b60ff168152602001838103835286818151815260200191508051906020019060200280838360005b83811015613be8578181015183820152602001613bd0565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015613c27578181015183820152602001613c0f565b50505050905001965050505050505060405180910390a250506140bb565b6000613c518284612fcb565b9050613c6a8966470de4df82000063ffffffff61300316565b81101580613c8e5750613c8a8966470de4df82000063ffffffff612aae16565b8111155b80613ca95750686194049f30f7200000613ca78461305d565b105b15613cc257505060016040870152506140c29350505050565b8e608001516001600160a01b0316632be212608e838e8e6040518563ffffffff1660e01b815260040180856001600160a01b03166001600160a01b03168152602001848152602001836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b03168152602001945050505050600060405180830381600087803b158015613d5a57600080fd5b505af1158015613d6e573d6000803e3d6000fd5b50505050600660009054906101000a90046001600160a01b03166001600160a01b03166331bafd9b8e856040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613de757600080fd5b505af1158015613dfb573d6000803e3d6000fd5b506000925050505b875151811015613e4457868181518110613e1957fe5b602002602001015188602001518281518110613e3157fe5b6020908102919091010152600101613e03565b50600660009054906101000a90046001600160a01b03166001600160a01b0316631843bf888e89600001518a602001516040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b031681526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015613edf578181015183820152602001613ec7565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015613f1e578181015183820152602001613f06565b5050505090500195505050505050600060405180830381600087803b158015613f4657600080fd5b505af1158015613f5a573d6000803e3d6000fd5b50505050600660009054906101000a90046001600160a01b03166001600160a01b03166318f2817a8e6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b03168152602001915050600060405180830381600087803b158015613fcb57600080fd5b505af1158015613fdf573d6000803e3d6000fd5b505050508c6001600160a01b0316600080516020614a1683398151915284896000015189600360405180858152602001806020018060200184600381111561402357fe5b60ff168152602001838103835286818151815260200191508051906020019060200280838360005b8381101561406357818101518382015260200161404b565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156140a257818101518382015260200161408a565b50505050905001965050505050505060405180910390a2505b5050505050505b979650505050505050565b6140d5614925565b6140dd614925565b6000805460408051634eb5750560e11b815290516001600160a01b0390921692639d6aea0a92600480840193829003018186803b15801561411d57600080fd5b505afa158015614131573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561415a57600080fd5b8101908080516040519392919084600160201b82111561417957600080fd5b90830190602082018581111561418e57600080fd5b82518660208202830111600160201b821117156141aa57600080fd5b82525081516020918201928201910280838360005b838110156141d75781810151838201526020016141bf565b5050505091909101604052505050818352505167ffffffffffffffff8111801561420057600080fd5b5060405190808252806020026020018201604052801561422a578160200160208202803683370190505b5060208201526000805b8551518110156143475760008054875180516001600160a01b039092169163b31610db91908590811061426357fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156142b157600080fd5b505afa1580156142c5573d6000803e3d6000fd5b505050506040513d60208110156142db57600080fd5b505160208801518051919250600091849081106142f457fe5b6020026020010151111561433e57602087015180516001909401938390811061431957fe5b60200260200101518460200151828151811061433157fe5b6020026020010181815250505b50600101614234565b5060005b8451518110156144a75760008054865180516001600160a01b039092169163b31610db91908590811061437a57fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156143c857600080fd5b505afa1580156143dc573d6000803e3d6000fd5b505050506040513d60208110156143f257600080fd5b5051602087015180519192506000918490811061440b57fe5b6020026020010151111561449e578360200151818151811061442957fe5b602002602001015160001415614440576001909201915b6144818660200151838151811061445357fe5b60200260200101518560200151838151811061446b57fe5b602002602001015161300390919063ffffffff16565b8460200151828151811061449157fe5b6020026020010181815250505b5060010161434b565b5060608167ffffffffffffffff811180156144c157600080fd5b506040519080825280602002602001820160405280156144eb578160200160208202803683370190505b50905060608267ffffffffffffffff8111801561450757600080fd5b50604051908082528060200260200182016040528015614531578160200160208202803683370190505b5090506000805b8551518110156145d85760008660200151828151811061455457fe5b602002602001015111156145d057855180518290811061457057fe5b602002602001015184838151811061458457fe5b6001600160a01b039092166020928302919091018201528601518051829081106145aa57fe5b60200260200101518383815181106145be57fe5b60209081029190910101526001909101905b600101614538565b5050908452602084015250909392505050565b6000806145f6611be9565b90506000614602611acd565b9050611bd38282612fcb565b6000818484111561469d5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561466257818101518382015260200161464a565b50505050905090810190601f16801561468f5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600081836146f45760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561466257818101518382015260200161464a565b50600083858161470057fe5b0495945050505050565b600080614729670de0b6b3a7640000610b09868663ffffffff612b0d16565b9050828110612af05760405162461bcd60e51b8152600401808060200182810382526036815260200180614a646036913960400191505060405180910390fd5b6000805b8251518110156148525760008054845180516001600160a01b039092169163cacabd2791908590811061479c57fe5b6020026020010151866020015185815181106147b457fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b031681526020018281526020019250505060206040518083038186803b15801561480957600080fd5b505afa15801561481d573d6000803e3d6000fd5b505050506040513d602081101561483357600080fd5b50519050614847838263ffffffff61300316565b92505060010161476d565b50919050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6040518060e0016040528060008152602001600081526020016148b5614925565b8152602001600081526020016000815260200160008152602001600081525090565b60408051608081018252600080825260208201819052918101829052606081019190915290565b604051806060016040528060008152602001614918614925565b8152600060209091015290565b60405180604001604052806060815260200160608152509056fe54726f76654d616e616765723a2052657175657374656420726564656d7074696f6e20616d6f756e74206d757374206265203c3d20757365722773205955534420746f6b656e2062616c616e6365436f6c6c61746572616c20746f2072656465656d206e6f7420666f756e6420696e2074726f766554726f76654d616e616765723a20526564656d7074696f6e7320617265206e6f7420616c6c6f77656420647572696e6720626f6f747374726170207068617365536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7798c96bbf8d2aa6be678259387225d169084fca483ed9598bde8ea258036bcacf54726f76654d616e616765723a20416d6f756e74206d7573742062652067726561746572207468616e207a65726f54726f76654d616e616765723a2046656520776f756c642065617420757020616c6c2072657475726e656420636f6c6c61746572616c54726f76654d616e616765723a2043616e6e6f742072656465656d207768656e20544352203c204d4352496e76616c6964207061727469616c20726564656d7074696f6e2068696e74206f722072656d61696e696e67206465627420697320746f6f206c6f77a2646970667358221220cb433487f07b2de0e6ff8b2ba009b5ccdb6de2f42953160d1c3d5a2f0901b4fb64736f6c634300060b0033

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading