ETH Price: $1,905.37 (-0.92%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Redeem Collatera...183291702023-10-11 19:06:59518 days ago1697051219IN
0xe103C279...17bF3e559
0 ETH0.00261978.40676281
Redeem Collatera...183290362023-10-11 18:39:35518 days ago1697049575IN
0xe103C279...17bF3e559
0 ETH0.003701069.37939638
Redeem Collatera...183289632023-10-11 18:24:35518 days ago1697048675IN
0xe103C279...17bF3e559
0 ETH0.0027884411.71407299
Liquidate183009292023-10-07 20:14:59522 days ago1696709699IN
0xe103C279...17bF3e559
0 ETH0.000132785.48372609
Set Addresses182818722023-10-05 4:17:23524 days ago1696479443IN
0xe103C279...17bF3e559
0 ETH0.001746676.07816704

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
TroveManager

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 100 runs

Other Settings:
default evmVersion
File 1 of 21 : TroveManager.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "./Interfaces/ITroveManager.sol";
import "./Interfaces/IStabilityPool.sol";
import "./Interfaces/ICollSurplusPool.sol";
import "./Interfaces/IGasPool.sol";
import "./Interfaces/ITHUSDToken.sol";
import "./Interfaces/ISortedTroves.sol";
import "./Interfaces/IPCV.sol";
import "./Dependencies/LiquityBase.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";

contract TroveManager is LiquityBase, Ownable, CheckContract, ITroveManager {

    string constant public NAME = "TroveManager";

    // --- Connected contract declarations ---

    address public borrowerOperationsAddress;

    IStabilityPool public override stabilityPool;

    address gasPoolAddress;

    ICollSurplusPool collSurplusPool;

    ITHUSDToken public override thusdToken;

    IPCV public override pcv;

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

    // --- Data structures ---

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

    /*
    * 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 constant public BETA = 2;

    uint256 public baseRate;

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

    // Store the necessary data for a trove
    struct Trove {
        uint256 debt;
        uint256 coll;
        uint256 stake;
        Status status;
        uint128 arrayIndex;
    }

    mapping (address => Trove) public Troves;

    uint256 public totalStakes;

    // Snapshot of the value of totalStakes, taken immediately after the latest liquidation
    uint256 public totalStakesSnapshot;

    // Snapshot of the total collateral across the ActivePool and DefaultPool, immediately after the latest liquidation.
    uint256 public totalCollateralSnapshot;

    /*
    * L_Collateral and L_THUSDDebt track the sums of accumulated liquidation rewards per unit staked. During its lifetime, each stake earns:
    *
    * An collateral gain of ( stake * [L_Collateral - L_Collateral(0)] )
    * A THUSDDebt increase  of ( stake * [L_THUSDDebt - L_THUSDDebt(0)] )
    *
    * Where L_Collateral(0) and L_THUSDDebt(0) are snapshots of L_Collateral and L_THUSDDebt for the active Trove taken at the instant the stake was made
    */
    uint256 public L_Collateral;
    uint256 public L_THUSDDebt;

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

    // Object containing the collateral and THUSD snapshots for a given active trove
    struct RewardSnapshot { uint256 collateral; uint256 THUSDDebt;}

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

    // Error trackers for the trove redistribution calculation
    uint256 public lastCollateralError_Redistribution;
    uint256 public lastTHUSDDebtError_Redistribution;

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

    struct LocalVariables_OuterLiquidationFunction {
        uint256 price;
        uint256 THUSDInStabPool;
        bool recoveryModeAtStart;
        uint256 liquidatedDebt;
        uint256 liquidatedColl;
    }

    struct LocalVariables_InnerSingleLiquidateFunction {
        uint256 collToLiquidate;
        uint256 pendingDebtReward;
        uint256 pendingCollReward;
    }

    struct LocalVariables_LiquidationSequence {
        uint256 remainingTHUSDInStabPool;
        uint256 i;
        uint256 ICR;
        address user;
        bool backToNormalMode;
        uint256 entireSystemDebt;
        uint256 entireSystemColl;
    }

    struct LiquidationValues {
        uint256 entireTroveDebt;
        uint256 entireTroveColl;
        uint256 collGasCompensation;
        uint256 THUSDGasCompensation;
        uint256 debtToOffset;
        uint256 collToSendToSP;
        uint256 debtToRedistribute;
        uint256 collToRedistribute;
        uint256 collSurplus;
    }

    struct LiquidationTotals {
        uint256 totalCollInSequence;
        uint256 totalDebtInSequence;
        uint256 totalCollGasCompensation;
        uint256 totalTHUSDGasCompensation;
        uint256 totalDebtToOffset;
        uint256 totalCollToSendToSP;
        uint256 totalDebtToRedistribute;
        uint256 totalCollToRedistribute;
        uint256 totalCollSurplus;
    }

    struct ContractsCache {
        IActivePool activePool;
        IDefaultPool defaultPool;
        ITHUSDToken thusdToken;
        IPCV pcv;
        ISortedTroves sortedTroves;
        ICollSurplusPool collSurplusPool;
        address gasPoolAddress;
    }
    // --- Variable container structs for redemptions ---

    struct RedemptionTotals {
        uint256 remainingTHUSD;
        uint256 totalTHUSDToRedeem;
        uint256 totalCollateralDrawn;
        uint256 collateralFee;
        uint256 collateralToSendToRedeemer;
        uint256 decayedBaseRate;
        uint256 price;
        uint256 totalTHUSDDebtAtStart;
    }

    struct SingleRedemptionValues {
        uint256 THUSDLot;
        uint256 collateralLot;
        bool cancelledPartial;
    }

    // --- Events ---
    event TroveUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 _stake, TroveManagerOperation _operation);
    event TroveLiquidated(address indexed _borrower, uint256 _debt, uint256 _coll, TroveManagerOperation _operation);

    enum TroveManagerOperation {
        applyPendingRewards,
        liquidateInNormalMode,
        liquidateInRecoveryMode,
        redeemCollateral
    }


    // --- Dependency setter ---

    function setAddresses(
        address _borrowerOperationsAddress,
        address _activePoolAddress,
        address _defaultPoolAddress,
        address _stabilityPoolAddress,
        address _gasPoolAddress,
        address _collSurplusPoolAddress,
        address _priceFeedAddress,
        address _thusdTokenAddress,
        address _sortedTrovesAddress,
        address _pcvAddress
    )
        external
        override
        onlyOwner
    {
        checkContract(_borrowerOperationsAddress);
        checkContract(_activePoolAddress);
        checkContract(_defaultPoolAddress);
        checkContract(_stabilityPoolAddress);
        checkContract(_gasPoolAddress);
        checkContract(_collSurplusPoolAddress);
        checkContract(_priceFeedAddress);
        checkContract(_thusdTokenAddress);
        checkContract(_sortedTrovesAddress);
        checkContract(_pcvAddress);

        borrowerOperationsAddress = _borrowerOperationsAddress;
        activePool = IActivePool(_activePoolAddress);
        defaultPool = IDefaultPool(_defaultPoolAddress);
        stabilityPool = IStabilityPool(_stabilityPoolAddress);
        gasPoolAddress = _gasPoolAddress;
        collSurplusPool = ICollSurplusPool(_collSurplusPoolAddress);
        priceFeed = IPriceFeed(_priceFeedAddress);
        thusdToken = ITHUSDToken(_thusdTokenAddress);
        sortedTroves = ISortedTroves(_sortedTrovesAddress);
        pcv = IPCV(_pcvAddress);

        emit BorrowerOperationsAddressChanged(_borrowerOperationsAddress);
        emit ActivePoolAddressChanged(_activePoolAddress);
        emit DefaultPoolAddressChanged(_defaultPoolAddress);
        emit StabilityPoolAddressChanged(_stabilityPoolAddress);
        emit GasPoolAddressChanged(_gasPoolAddress);
        emit CollSurplusPoolAddressChanged(_collSurplusPoolAddress);
        emit PriceFeedAddressChanged(_priceFeedAddress);
        emit THUSDTokenAddressChanged(_thusdTokenAddress);
        emit SortedTrovesAddressChanged(_sortedTrovesAddress);
        emit PCVAddressChanged(_pcvAddress);

        _renounceOwnership();
    }

    // --- Getters ---

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

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

    // --- Trove Liquidation functions ---

    // Single liquidation function. Closes the trove if its ICR is lower than the minimum collateral ratio.
    function liquidate(address _borrower) external override {
        _requireTroveIsActive(_borrower);

        address[] memory borrowers = new address[](1);
        borrowers[0] = _borrower;
        batchLiquidateTroves(borrowers);
    }

    // --- Inner single liquidation functions ---

    // Liquidate one trove, in Normal Mode.
    function _liquidateNormalMode(
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        address _borrower,
        uint256 _THUSDInStabPool
    )
        internal
        returns (LiquidationValues memory singleLiquidation)
    {
        LocalVariables_InnerSingleLiquidateFunction memory vars;

        (singleLiquidation.entireTroveDebt,
        singleLiquidation.entireTroveColl,
        vars.pendingDebtReward,
        vars.pendingCollReward) = getEntireDebtAndColl(_borrower);

        _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward);
        _removeStake(_borrower);

        singleLiquidation.collGasCompensation = _getCollGasCompensation(singleLiquidation.entireTroveColl);
        singleLiquidation.THUSDGasCompensation = THUSD_GAS_COMPENSATION;
        uint256 collToLiquidate = singleLiquidation.entireTroveColl - singleLiquidation.collGasCompensation;

        (singleLiquidation.debtToOffset,
        singleLiquidation.collToSendToSP,
        singleLiquidation.debtToRedistribute,
        singleLiquidation.collToRedistribute) = _getOffsetAndRedistributionVals(singleLiquidation.entireTroveDebt, collToLiquidate, _THUSDInStabPool);

        _closeTrove(_borrower, Status.closedByLiquidation);
        emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, TroveManagerOperation.liquidateInNormalMode);
        emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInNormalMode);
        return singleLiquidation;
    }

    // Liquidate one trove, in Recovery Mode.
    function _liquidateRecoveryMode(
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        address _borrower,
        uint256 _ICR,
        uint256 _THUSDInStabPool,
        uint256 _TCR,
        uint256 _price
    )
        internal
        returns (LiquidationValues memory singleLiquidation)
    {
        LocalVariables_InnerSingleLiquidateFunction memory vars;
        if (TroveOwners.length <= 1) {return singleLiquidation;} // don't liquidate if last trove
        (singleLiquidation.entireTroveDebt,
        singleLiquidation.entireTroveColl,
        vars.pendingDebtReward,
        vars.pendingCollReward) = getEntireDebtAndColl(_borrower);

        singleLiquidation.collGasCompensation = _getCollGasCompensation(singleLiquidation.entireTroveColl);
        singleLiquidation.THUSDGasCompensation = THUSD_GAS_COMPENSATION;
        vars.collToLiquidate = singleLiquidation.entireTroveColl - singleLiquidation.collGasCompensation;

        // If ICR <= 100%, purely redistribute the Trove across all active Troves
        if (_ICR <= _100pct) {
            _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward);
            _removeStake(_borrower);

            singleLiquidation.debtToOffset = 0;
            singleLiquidation.collToSendToSP = 0;
            singleLiquidation.debtToRedistribute = singleLiquidation.entireTroveDebt;
            singleLiquidation.collToRedistribute = vars.collToLiquidate;

            _closeTrove(_borrower, Status.closedByLiquidation);
            emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, TroveManagerOperation.liquidateInRecoveryMode);
            emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInRecoveryMode);

        // If 100% < ICR < MCR, offset as much as possible, and redistribute the remainder
        } else if ((_ICR > _100pct) && (_ICR < MCR)) {
             _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward);
            _removeStake(_borrower);

            (singleLiquidation.debtToOffset,
            singleLiquidation.collToSendToSP,
            singleLiquidation.debtToRedistribute,
            singleLiquidation.collToRedistribute) = _getOffsetAndRedistributionVals(singleLiquidation.entireTroveDebt, vars.collToLiquidate, _THUSDInStabPool);

            _closeTrove(_borrower, Status.closedByLiquidation);
            emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, TroveManagerOperation.liquidateInRecoveryMode);
            emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInRecoveryMode);
        /*
        * If 110% <= ICR < current TCR (accounting for the preceding liquidations in the current sequence)
        * and there is THUSD in the Stability Pool, only offset, with no redistribution,
        * but at a capped rate of 1.1 and only if the whole debt can be liquidated.
        * The remainder due to the capped rate will be claimable as collateral surplus.
        */
        } else if ((_ICR >= MCR) && (_ICR < _TCR) && (singleLiquidation.entireTroveDebt <= _THUSDInStabPool)) {
            _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward);
            assert(_THUSDInStabPool != 0);

            _removeStake(_borrower);
            singleLiquidation = _getCappedOffsetVals(singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, _price);

            _closeTrove(_borrower, Status.closedByLiquidation);
            if (singleLiquidation.collSurplus > 0) {
                collSurplusPool.accountSurplus(_borrower, singleLiquidation.collSurplus);
            }

            emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.collToSendToSP, TroveManagerOperation.liquidateInRecoveryMode);
            emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInRecoveryMode);

        } else { // if (_ICR >= MCR && ( _ICR >= _TCR || singleLiquidation.entireTroveDebt > _THUSDInStabPool))
            LiquidationValues memory zeroVals;
            return zeroVals;
        }

        return singleLiquidation;
    }

    /* In a full liquidation, returns the values for a trove's coll and debt to be offset, and coll and debt to be
    * redistributed to active troves.
    */
    function _getOffsetAndRedistributionVals
    (
        uint256 _debt,
        uint256 _coll,
        uint256 _THUSDInStabPool
    )
        internal
        pure
        returns (uint256 debtToOffset, uint256 collToSendToSP, uint256 debtToRedistribute, uint256 collToRedistribute)
    {
        if (_THUSDInStabPool > 0) {
        /*
        * Offset as much debt & collateral as possible against the Stability Pool, and redistribute the remainder
        * between all active troves.
        *
        *  If the trove's debt is larger than the deposited THUSD in the Stability Pool:
        *
        *  - Offset an amount of the trove's debt equal to the THUSD in the Stability Pool
        *  - Send a fraction of the trove's collateral to the Stability Pool, equal to the fraction of its offset debt
        *
        */
            debtToOffset = LiquityMath._min(_debt, _THUSDInStabPool);
            collToSendToSP = _coll * debtToOffset / _debt;
            debtToRedistribute = _debt - debtToOffset;
            collToRedistribute = _coll - collToSendToSP;
        } else {
            debtToOffset = 0;
            collToSendToSP = 0;
            debtToRedistribute = _debt;
            collToRedistribute = _coll;
        }
    }

    /*
    *  Get its offset coll/debt and collateral gas comp, and close the trove.
    */
    function _getCappedOffsetVals
    (
        uint256 _entireTroveDebt,
        uint256 _entireTroveColl,
        uint256 _price
    )
        internal
        pure
        returns (LiquidationValues memory singleLiquidation)
    {
        singleLiquidation.entireTroveDebt = _entireTroveDebt;
        singleLiquidation.entireTroveColl = _entireTroveColl;
        uint256 cappedCollPortion = _entireTroveDebt * MCR / _price;

        singleLiquidation.collGasCompensation = _getCollGasCompensation(cappedCollPortion);
        singleLiquidation.THUSDGasCompensation = THUSD_GAS_COMPENSATION;

        singleLiquidation.debtToOffset = _entireTroveDebt;
        singleLiquidation.collToSendToSP = cappedCollPortion - singleLiquidation.collGasCompensation;
        singleLiquidation.collSurplus = _entireTroveColl - cappedCollPortion;
        singleLiquidation.debtToRedistribute = 0;
        singleLiquidation.collToRedistribute = 0;
    }

    /*
    * Liquidate a sequence of troves. Closes a maximum number of n under-collateralized Troves,
    * starting from the one with the lowest collateral ratio in the system, and moving upwards
    */
    function liquidateTroves(uint256 _n) external override {
        ContractsCache memory contractsCache = ContractsCache(
            activePool,
            defaultPool,
            ITHUSDToken(address(0)),
            IPCV(address(0)),
            sortedTroves,
            ICollSurplusPool(address(0)),
            address(0)
        );
        IStabilityPool stabilityPoolCached = stabilityPool;

        LocalVariables_OuterLiquidationFunction memory vars;

        LiquidationTotals memory totals;

        vars.price = priceFeed.fetchPrice();
        vars.THUSDInStabPool = stabilityPoolCached.getTotalTHUSDDeposits();
        vars.recoveryModeAtStart = _checkRecoveryMode(vars.price);

        // Perform the appropriate liquidation sequence - tally the values, and obtain their totals
        if (vars.recoveryModeAtStart) {
            totals = _getTotalsFromLiquidateTrovesSequence_RecoveryMode(contractsCache, vars.price, vars.THUSDInStabPool, _n);
        } else { // if !vars.recoveryModeAtStart
            totals = _getTotalsFromLiquidateTrovesSequence_NormalMode(contractsCache.activePool, contractsCache.defaultPool, vars.price, vars.THUSDInStabPool, _n);
        }

        require(totals.totalDebtInSequence > 0, "TroveManager: nothing to liquidate");

        // Move liquidated collateral and THUSD to the appropriate pools
        stabilityPoolCached.offset(totals.totalDebtToOffset, totals.totalCollToSendToSP);
        _redistributeDebtAndColl(contractsCache.activePool, contractsCache.defaultPool, totals.totalDebtToRedistribute, totals.totalCollToRedistribute);
        if (totals.totalCollSurplus > 0) {
            contractsCache.activePool.sendCollateral(address(collSurplusPool), totals.totalCollSurplus);
        }

        // Update system snapshots
        _updateSystemSnapshots_excludeCollRemainder(contractsCache.activePool, totals.totalCollGasCompensation);

        vars.liquidatedDebt = totals.totalDebtInSequence;
        vars.liquidatedColl = totals.totalCollInSequence - totals.totalCollGasCompensation - totals.totalCollSurplus;
        emit Liquidation(vars.liquidatedDebt, vars.liquidatedColl, totals.totalCollGasCompensation, totals.totalTHUSDGasCompensation);

        // Send gas compensation to caller
        _sendGasCompensation(contractsCache.activePool, msg.sender, totals.totalTHUSDGasCompensation, totals.totalCollGasCompensation);
    }

    /*
    * This function is used when the liquidateTroves sequence starts during Recovery Mode. However, it
    * handle the case where the system *leaves* Recovery Mode, part way through the liquidation sequence
    */
    function _getTotalsFromLiquidateTrovesSequence_RecoveryMode
    (
        ContractsCache memory _contractsCache,
        uint256 _price,
        uint256 _THUSDInStabPool,
        uint256 _n
    )
        internal
        returns(LiquidationTotals memory totals)
    {
        LocalVariables_LiquidationSequence memory vars;
        LiquidationValues memory singleLiquidation;

        vars.remainingTHUSDInStabPool = _THUSDInStabPool;
        vars.backToNormalMode = false;
        vars.entireSystemDebt = getEntireSystemDebt();
        vars.entireSystemColl = getEntireSystemColl();

        vars.user = _contractsCache.sortedTroves.getLast();
        address firstUser = _contractsCache.sortedTroves.getFirst();
        for (vars.i = 0; vars.i < _n && vars.user != firstUser; vars.i++) {
            // we need to cache it, because current user is likely going to be deleted
            address nextUser = _contractsCache.sortedTroves.getPrev(vars.user);

            vars.ICR = getCurrentICR(vars.user, _price);

            if (!vars.backToNormalMode) {
                // Break the loop if ICR is greater than MCR and Stability Pool is empty
                if (vars.ICR >= MCR && vars.remainingTHUSDInStabPool == 0) { break; }

                uint256 TCR = LiquityMath._computeCR(vars.entireSystemColl, vars.entireSystemDebt, _price);

                singleLiquidation = _liquidateRecoveryMode(_contractsCache.activePool, _contractsCache.defaultPool, vars.user, vars.ICR, vars.remainingTHUSDInStabPool, TCR, _price);

                // Update aggregate trackers
                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;
                vars.entireSystemDebt -= singleLiquidation.debtToOffset;
                vars.entireSystemColl -= singleLiquidation.collToSendToSP
                    + singleLiquidation.collGasCompensation
                    + singleLiquidation.collSurplus;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);

                vars.backToNormalMode = !_checkPotentialRecoveryMode(vars.entireSystemColl, vars.entireSystemDebt, _price);
            }
            else if (vars.backToNormalMode && vars.ICR < MCR) {
                singleLiquidation = _liquidateNormalMode(_contractsCache.activePool, _contractsCache.defaultPool, vars.user, vars.remainingTHUSDInStabPool);

                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);

            }  else break;  // break if the loop reaches a Trove with ICR >= MCR

            vars.user = nextUser;
        }
    }

    function _getTotalsFromLiquidateTrovesSequence_NormalMode
    (
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        uint256 _price,
        uint256 _THUSDInStabPool,
        uint256 _n
    )
        internal
        returns(LiquidationTotals memory totals)
    {
        LocalVariables_LiquidationSequence memory vars;
        LiquidationValues memory singleLiquidation;
        ISortedTroves sortedTrovesCached = sortedTroves;

        vars.remainingTHUSDInStabPool = _THUSDInStabPool;

        for (vars.i = 0; vars.i < _n; vars.i++) {
            vars.user = sortedTrovesCached.getLast();
            vars.ICR = getCurrentICR(vars.user, _price);

            if (vars.ICR < MCR) {
                singleLiquidation = _liquidateNormalMode(_activePool, _defaultPool, vars.user, vars.remainingTHUSDInStabPool);

                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);

            } else break;  // break if the loop reaches a Trove with ICR >= MCR
        }
    }

    /*
    * Attempt to liquidate a custom list of troves provided by the caller.
    */
    function batchLiquidateTroves(address[] memory _troveArray) public override {
        require(_troveArray.length != 0, "TroveManager: Calldata address array must not be empty");

        IActivePool activePoolCached = activePool;
        IDefaultPool defaultPoolCached = defaultPool;
        IStabilityPool stabilityPoolCached = stabilityPool;

        LocalVariables_OuterLiquidationFunction memory vars;
        LiquidationTotals memory totals;

        vars.price = priceFeed.fetchPrice();
        vars.THUSDInStabPool = stabilityPoolCached.getTotalTHUSDDeposits();
        vars.recoveryModeAtStart = _checkRecoveryMode(vars.price);

        // Perform the appropriate liquidation sequence - tally values and obtain their totals.
        if (vars.recoveryModeAtStart) {
            totals = _getTotalFromBatchLiquidate_RecoveryMode(activePoolCached, defaultPoolCached, vars.price, vars.THUSDInStabPool, _troveArray);
        } else {  //  if !vars.recoveryModeAtStart
            totals = _getTotalsFromBatchLiquidate_NormalMode(activePoolCached, defaultPoolCached, vars.price, vars.THUSDInStabPool, _troveArray);
        }

        require(totals.totalDebtInSequence > 0, "TroveManager: nothing to liquidate");

        // Move liquidated collateral and THUSD to the appropriate pools
        stabilityPoolCached.offset(totals.totalDebtToOffset, totals.totalCollToSendToSP);
        _redistributeDebtAndColl(activePoolCached, defaultPoolCached, totals.totalDebtToRedistribute, totals.totalCollToRedistribute);
        if (totals.totalCollSurplus > 0) {
            activePoolCached.sendCollateral(address(collSurplusPool), totals.totalCollSurplus);
        }

        // Update system snapshots
        _updateSystemSnapshots_excludeCollRemainder(activePoolCached, totals.totalCollGasCompensation);

        vars.liquidatedDebt = totals.totalDebtInSequence;
        vars.liquidatedColl = totals.totalCollInSequence - totals.totalCollGasCompensation - totals.totalCollSurplus;
        emit Liquidation(vars.liquidatedDebt, vars.liquidatedColl, totals.totalCollGasCompensation, totals.totalTHUSDGasCompensation);

        // Send gas compensation to caller
        _sendGasCompensation(activePoolCached, msg.sender, totals.totalTHUSDGasCompensation, totals.totalCollGasCompensation);
    }

    /*
    * This function is used when the batch liquidation sequence starts during Recovery Mode. However, it
    * handle the case where the system *leaves* Recovery Mode, part way through the liquidation sequence
    */
    function _getTotalFromBatchLiquidate_RecoveryMode
    (
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        uint256 _price,
        uint256 _THUSDInStabPool,
        address[] memory _troveArray
    )
        internal
        returns(LiquidationTotals memory totals)
    {
        LocalVariables_LiquidationSequence memory vars;
        LiquidationValues memory singleLiquidation;

        vars.remainingTHUSDInStabPool = _THUSDInStabPool;
        vars.backToNormalMode = false;
        vars.entireSystemDebt = getEntireSystemDebt();
        vars.entireSystemColl = getEntireSystemColl();

        for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {
            vars.user = _troveArray[vars.i];
            // Skip non-active troves
            if (Troves[vars.user].status != Status.active) { continue; }
            vars.ICR = getCurrentICR(vars.user, _price);

            if (!vars.backToNormalMode) {

                // Skip this trove if ICR is greater than MCR and Stability Pool is empty
                if (vars.ICR >= MCR && vars.remainingTHUSDInStabPool == 0) { continue; }

                uint256 TCR = LiquityMath._computeCR(vars.entireSystemColl, vars.entireSystemDebt, _price);

                singleLiquidation = _liquidateRecoveryMode(_activePool, _defaultPool, vars.user, vars.ICR, vars.remainingTHUSDInStabPool, TCR, _price);

                // Update aggregate trackers
                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;
                vars.entireSystemDebt -= singleLiquidation.debtToOffset;
                vars.entireSystemColl -= singleLiquidation.collToSendToSP
                    + singleLiquidation.collGasCompensation
                    + singleLiquidation.collSurplus;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);

                vars.backToNormalMode = !_checkPotentialRecoveryMode(vars.entireSystemColl, vars.entireSystemDebt, _price);
            }

            else if (vars.backToNormalMode && vars.ICR < MCR) {
                singleLiquidation = _liquidateNormalMode(_activePool, _defaultPool, vars.user, vars.remainingTHUSDInStabPool);
                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);

            } else continue; // In Normal Mode skip troves with ICR >= MCR
        }
    }

    function _getTotalsFromBatchLiquidate_NormalMode
    (
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        uint256 _price,
        uint256 _THUSDInStabPool,
        address[] memory _troveArray
    )
        internal
        returns(LiquidationTotals memory totals)
    {
        LocalVariables_LiquidationSequence memory vars;
        LiquidationValues memory singleLiquidation;

        vars.remainingTHUSDInStabPool = _THUSDInStabPool;

        for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {
            vars.user = _troveArray[vars.i];
            vars.ICR = getCurrentICR(vars.user, _price);

            if (vars.ICR < MCR) {
                singleLiquidation = _liquidateNormalMode(_activePool, _defaultPool, vars.user, vars.remainingTHUSDInStabPool);
                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);
            }
        }
    }

    // --- Liquidation helper functions ---

    function _addLiquidationValuesToTotals(LiquidationTotals memory oldTotals, LiquidationValues memory singleLiquidation)
    internal pure returns(LiquidationTotals memory newTotals) {

        // Tally all the values with their respective running totals
        newTotals.totalCollGasCompensation = oldTotals.totalCollGasCompensation + singleLiquidation.collGasCompensation;
        newTotals.totalTHUSDGasCompensation = oldTotals.totalTHUSDGasCompensation + singleLiquidation.THUSDGasCompensation;
        newTotals.totalDebtInSequence = oldTotals.totalDebtInSequence + singleLiquidation.entireTroveDebt;
        newTotals.totalCollInSequence = oldTotals.totalCollInSequence + singleLiquidation.entireTroveColl;
        newTotals.totalDebtToOffset = oldTotals.totalDebtToOffset + singleLiquidation.debtToOffset;
        newTotals.totalCollToSendToSP = oldTotals.totalCollToSendToSP + singleLiquidation.collToSendToSP;
        newTotals.totalDebtToRedistribute = oldTotals.totalDebtToRedistribute + singleLiquidation.debtToRedistribute;
        newTotals.totalCollToRedistribute = oldTotals.totalCollToRedistribute + singleLiquidation.collToRedistribute;
        newTotals.totalCollSurplus = oldTotals.totalCollSurplus + singleLiquidation.collSurplus;

        return newTotals;
    }

    function _sendGasCompensation(IActivePool _activePool, address _liquidator, uint256 _THUSD, uint256 _collateral) internal {
        if (_THUSD > 0) {
            IGasPool(gasPoolAddress).sendTHUSD(_liquidator, _THUSD);
        }

        if (_collateral > 0) {
            _activePool.sendCollateral(_liquidator, _collateral);
        }
    }

    // Move a Trove's pending debt and collateral rewards from distributions, from the Default Pool to the Active Pool
    function _movePendingTroveRewardsToActivePool(IActivePool _activePool, IDefaultPool _defaultPool, uint256 _THUSD, uint256 _collateral) internal {
        _defaultPool.decreaseTHUSDDebt(_THUSD);
        _activePool.increaseTHUSDDebt(_THUSD);
        _defaultPool.sendCollateralToActivePool(_collateral);
    }

    // --- Redemption functions ---

    // Redeem as much collateral as possible from _borrower's Trove in exchange for THUSD up to _maxTHUSDamount
    function _redeemCollateralFromTrove(
        ContractsCache memory _contractsCache,
        address _borrower,
        uint256 _maxTHUSDamount,
        uint256 _price,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintNICR
    )
        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.THUSDLot = LiquityMath._min(_maxTHUSDamount, Troves[_borrower].debt - THUSD_GAS_COMPENSATION);

        // Get the collateralLot of equivalent value in USD
        singleRedemption.collateralLot = singleRedemption.THUSDLot * DECIMAL_PRECISION / _price;

        // Decrease the debt and collateral of the current Trove according to the THUSD lot and corresponding collateral to send
        uint256 newDebt = Troves[_borrower].debt - singleRedemption.THUSDLot;
        uint256 newColl = Troves[_borrower].coll - singleRedemption.collateralLot;

        if (newDebt == THUSD_GAS_COMPENSATION) {
            // No debt left in the Trove (except for the liquidation reserve), therefore the trove gets closed
            _removeStake(_borrower);
            _closeTrove(_borrower, Status.closedByRedemption);
            _redeemCloseTrove(_contractsCache, _borrower, THUSD_GAS_COMPENSATION, newColl);
            emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.redeemCollateral);

        } else {
            uint256 newNICR = LiquityMath._computeNominalCR(newColl, newDebt);

            /*
            * If the provided hint is out of date, we bail since trying to reinsert without a good hint will almost
            * certainly result in running out of gas.
            *
            * If the resultant net debt of the partial is less than the minimum, net debt we bail.
            */
            if (newNICR != _partialRedemptionHintNICR || _getNetDebt(newDebt) < MIN_NET_DEBT) {
                singleRedemption.cancelledPartial = true;
                return singleRedemption;
            }

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

            Troves[_borrower].debt = newDebt;
            Troves[_borrower].coll = newColl;
            _updateStakeAndTotalStakes(_borrower);

            emit TroveUpdated(
                _borrower,
                newDebt, newColl,
                Troves[_borrower].stake,
                TroveManagerOperation.redeemCollateral
            );
        }

        return singleRedemption;
    }

    /*
    * Called when a full redemption occurs, and closes the trove.
    * The redeemer swaps (debt - liquidation reserve) THUSD for (debt - liquidation reserve) worth of collateral, so the THUSD liquidation reserve left corresponds to the remaining debt.
    * In order to close the trove, the THUSD 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 _THUSD, uint256 _collateral) internal {
        _contractsCache.thusdToken.burn(gasPoolAddress, _THUSD);
        // Update Active Pool THUSD, and send collateral to account
        _contractsCache.activePool.decreaseTHUSDDebt(_THUSD);

        // send collateral from Active Pool to CollSurplus Pool
        _contractsCache.collSurplusPool.accountSurplus(_borrower, _collateral);
        _contractsCache.activePool.sendCollateral(address(_contractsCache.collSurplusPool), _collateral);
    }

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

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

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

        _requireValidMaxFeePercentage(_maxFeePercentage);
        totals.price = priceFeed.fetchPrice();
        _requireTCRoverMCR(totals.price);
        _requireAmountGreaterThanZero(_THUSDamount);
        _requireTHUSDBalanceCoversRedemption(contractsCache.thusdToken, msg.sender, _THUSDamount);

        totals.totalTHUSDDebtAtStart = getEntireSystemDebt();
        totals.remainingTHUSD = _THUSDamount;
        address currentBorrower;

        if (_isValidFirstRedemptionHint(contractsCache.sortedTroves, _firstRedemptionHint, totals.price)) {
            currentBorrower = _firstRedemptionHint;
        } else {
            currentBorrower = contractsCache.sortedTroves.getLast();
            // Find the first trove with ICR >= MCR
            while (currentBorrower != address(0) && getCurrentICR(currentBorrower, totals.price) < MCR) {
                currentBorrower = contractsCache.sortedTroves.getPrev(currentBorrower);
            }
        }

        // Loop through the Troves starting from the one with lowest collateral ratio until _amount of THUSD is exchanged for collateral
        if (_maxIterations == 0) { _maxIterations = type(uint256).max; }
        while (currentBorrower != address(0) && totals.remainingTHUSD > 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);

            _applyPendingRewards(contractsCache.activePool, contractsCache.defaultPool, currentBorrower);

            SingleRedemptionValues memory singleRedemption = _redeemCollateralFromTrove(
                contractsCache,
                currentBorrower,
                totals.remainingTHUSD,
                totals.price,
                _upperPartialRedemptionHint,
                _lowerPartialRedemptionHint,
                _partialRedemptionHintNICR
            );

            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.totalTHUSDToRedeem  += singleRedemption.THUSDLot;
            totals.totalCollateralDrawn += singleRedemption.collateralLot;

            totals.remainingTHUSD -= singleRedemption.THUSDLot;
            currentBorrower = nextUserToCheck;
        }
        require(totals.totalCollateralDrawn > 0, "TroveManager: Unable to redeem any amount");

        // Decay the baseRate due to time passed, and then increase it according to the size of this redemption.
        // Use the saved total THUSD supply value, from before it was reduced by the redemption.
        _updateBaseRateFromRedemption(totals.totalCollateralDrawn, totals.price, totals.totalTHUSDDebtAtStart);

        // Calculate the collateral fee
        totals.collateralFee = _getRedemptionFee(totals.totalCollateralDrawn);

        _requireUserAcceptsFee(totals.collateralFee, totals.totalCollateralDrawn, _maxFeePercentage);

        // Send the collateral fee to the PCV contract
        contractsCache.activePool.sendCollateral(address(contractsCache.pcv), totals.collateralFee);

        totals.collateralToSendToRedeemer = totals.totalCollateralDrawn - totals.collateralFee;

        emit Redemption(_THUSDamount, totals.totalTHUSDToRedeem, totals.totalCollateralDrawn, totals.collateralFee);

        // Burn the total THUSD that is cancelled with debt, and send the redeemed collateral to msg.sender
        contractsCache.thusdToken.burn(msg.sender, totals.totalTHUSDToRedeem);
        // Update Active Pool THUSD, and send collateral to account
        contractsCache.activePool.decreaseTHUSDDebt(totals.totalTHUSDToRedeem);
        contractsCache.activePool.sendCollateral(msg.sender, totals.collateralToSendToRedeemer);
    }

    // --- Helper functions ---

    // Return the nominal collateral ratio (ICR) of a given Trove, without the price. Takes a trove's pending coll and debt rewards from redistributions into account.
    function getNominalICR(address _borrower) public view override returns (uint) {
        (uint256 currentCollateral, uint256 currentTHUSDDebt) = _getCurrentTroveAmounts(_borrower);

        uint256 NICR = LiquityMath._computeNominalCR(currentCollateral, currentTHUSDDebt);
        return NICR;
    }

    // Return the current collateral ratio (ICR) of a given Trove. Takes a trove's pending coll and debt rewards from redistributions into account.
    function getCurrentICR(address _borrower, uint256 _price) public view override returns (uint) {
        (uint256 currentCollateral, uint256 currentTHUSDDebt) = _getCurrentTroveAmounts(_borrower);

        uint256 ICR = LiquityMath._computeCR(currentCollateral, currentTHUSDDebt, _price);
        return ICR;
    }

    function _getCurrentTroveAmounts(address _borrower) internal view returns (uint, uint) {
        uint256 pendingCollateralReward = getPendingCollateralReward(_borrower);
        uint256 pendingTHUSDDebtReward = getPendingTHUSDDebtReward(_borrower);

        uint256 currentCollateral = Troves[_borrower].coll + pendingCollateralReward;
        uint256 currentTHUSDDebt = Troves[_borrower].debt + pendingTHUSDDebtReward;

        return (currentCollateral, currentTHUSDDebt);
    }

    function applyPendingRewards(address _borrower) external override {
        _requireCallerIsBorrowerOperations();
        return _applyPendingRewards(activePool, defaultPool, _borrower);
    }

    // Add the borrowers's coll and debt rewards earned from redistributions, to their Trove
    function _applyPendingRewards(IActivePool _activePool, IDefaultPool _defaultPool, address _borrower) internal {
        if (hasPendingRewards(_borrower)) {
            _requireTroveIsActive(_borrower);

            // Compute pending rewards
            uint256 pendingCollateralReward = getPendingCollateralReward(_borrower);
            uint256 pendingTHUSDDebtReward = getPendingTHUSDDebtReward(_borrower);

            // Apply pending rewards to trove's state
            Troves[_borrower].coll += pendingCollateralReward;
            Troves[_borrower].debt += pendingTHUSDDebtReward;

            _updateTroveRewardSnapshots(_borrower);

            // Transfer from DefaultPool to ActivePool
            _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, pendingTHUSDDebtReward, pendingCollateralReward);

            emit TroveUpdated(
                _borrower,
                Troves[_borrower].debt,
                Troves[_borrower].coll,
                Troves[_borrower].stake,
                TroveManagerOperation.applyPendingRewards
            );
        }
    }

    // Update borrower's snapshots of L_Collateral and L_THUSDDebt to reflect the current values
    function updateTroveRewardSnapshots(address _borrower) external override {
        _requireCallerIsBorrowerOperations();
       return _updateTroveRewardSnapshots(_borrower);
    }

    function _updateTroveRewardSnapshots(address _borrower) internal {
        rewardSnapshots[_borrower].collateral = L_Collateral;
        rewardSnapshots[_borrower].THUSDDebt = L_THUSDDebt;
        emit TroveSnapshotsUpdated(L_Collateral, L_THUSDDebt);
    }

    // Get the borrower's pending accumulated collateral reward, earned by their stake
    function getPendingCollateralReward(address _borrower) public view override returns (uint) {
        uint256 snapshotCollateral = rewardSnapshots[_borrower].collateral;
        uint256 rewardPerUnitStaked = L_Collateral - snapshotCollateral;

        if ( rewardPerUnitStaked == 0 || Troves[_borrower].status != Status.active) { return 0; }

        uint256 stake = Troves[_borrower].stake;

        uint256 pendingCollateralReward = stake * rewardPerUnitStaked / DECIMAL_PRECISION;

        return pendingCollateralReward;
    }

    // Get the borrower's pending accumulated THUSD reward, earned by their stake
    function getPendingTHUSDDebtReward(address _borrower) public view override returns (uint) {
        uint256 snapshotTHUSDDebt = rewardSnapshots[_borrower].THUSDDebt;
        uint256 rewardPerUnitStaked = L_THUSDDebt - snapshotTHUSDDebt;

        if ( rewardPerUnitStaked == 0 || Troves[_borrower].status != Status.active) { return 0; }

        uint256 stake =  Troves[_borrower].stake;

        uint256 pendingTHUSDDebtReward = stake * rewardPerUnitStaked / DECIMAL_PRECISION;

        return pendingTHUSDDebtReward;
    }

    function hasPendingRewards(address _borrower) public view override returns (bool) {
        /*
        * A Trove has pending rewards if its snapshot is less than the current rewards per-unit-staked sum:
        * this indicates that rewards have occured since the snapshot was made, and the user therefore has
        * pending rewards
        */
        if (Troves[_borrower].status != Status.active) {return false;}

        return (rewardSnapshots[_borrower].collateral < L_Collateral);
    }

    // Return the Troves entire debt and coll, including pending rewards from redistributions.
    function getEntireDebtAndColl(
        address _borrower
    )
        public
        view
        override
        returns (uint256 debt, uint256 coll, uint256 pendingTHUSDDebtReward, uint256 pendingCollateralReward)
    {
        debt = Troves[_borrower].debt;
        coll = Troves[_borrower].coll;

        pendingTHUSDDebtReward = getPendingTHUSDDebtReward(_borrower);
        pendingCollateralReward = getPendingCollateralReward(_borrower);

        debt += pendingTHUSDDebtReward;
        coll += pendingCollateralReward;
    }

    function removeStake(address _borrower) external override {
        _requireCallerIsBorrowerOperations();
        return _removeStake(_borrower);
    }

    // Remove borrower's stake from the totalStakes sum, and set their stake to 0
    function _removeStake(address _borrower) internal {
        uint256 stake = Troves[_borrower].stake;
        totalStakes -= stake;
        Troves[_borrower].stake = 0;
    }

    function updateStakeAndTotalStakes(address _borrower) external override returns (uint) {
        _requireCallerIsBorrowerOperations();
        return _updateStakeAndTotalStakes(_borrower);
    }

    // Update borrower's stake based on their latest collateral value
    function _updateStakeAndTotalStakes(address _borrower) internal returns (uint) {
        uint256 newStake = _computeNewStake(Troves[_borrower].coll);
        uint256 oldStake = Troves[_borrower].stake;
        Troves[_borrower].stake = newStake;

        totalStakes = totalStakes - oldStake + newStake;
        emit TotalStakesUpdated(totalStakes);

        return newStake;
    }

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

    function _redistributeDebtAndColl(IActivePool _activePool, IDefaultPool _defaultPool, uint256 _debt, uint256 _coll) internal {
        if (_debt == 0) { return; }

        /*
        * Add distributed coll and debt rewards-per-unit-staked to the running totals. Division uses a "feedback"
        * error correction, to keep the cumulative error low in the running totals L_Collateral and L_THUSDDebt:
        *
        * 1) Form numerators which compensate for the floor division errors that occurred the last time this
        * function was called.
        * 2) Calculate "per-unit-staked" ratios.
        * 3) Multiply each ratio back by its denominator, to reveal the current floor division error.
        * 4) Store these errors for use in the next correction when this function is called.
        * 5) Note: static analysis tools complain about this "division before multiplication", however, it is intended.
        */
        uint256 collateralNumerator = _coll * DECIMAL_PRECISION + lastCollateralError_Redistribution;
        uint256 THUSDDebtNumerator = _debt * DECIMAL_PRECISION + lastTHUSDDebtError_Redistribution;

        // Get the per-unit-staked terms
        uint256 collateralRewardPerUnitStaked = collateralNumerator / totalStakes;
        uint256 THUSDDebtRewardPerUnitStaked = THUSDDebtNumerator / totalStakes;

        lastCollateralError_Redistribution = collateralNumerator - (collateralRewardPerUnitStaked * totalStakes);
        lastTHUSDDebtError_Redistribution = THUSDDebtNumerator - (THUSDDebtRewardPerUnitStaked * totalStakes);

        // Add per-unit-staked terms to the running totals
        L_Collateral += collateralRewardPerUnitStaked;
        L_THUSDDebt += THUSDDebtRewardPerUnitStaked;

        emit LTermsUpdated(L_Collateral, L_THUSDDebt);

        // Transfer coll and debt from ActivePool to DefaultPool
        _activePool.decreaseTHUSDDebt(_debt);
        _defaultPool.increaseTHUSDDebt(_debt);
        _activePool.sendCollateral(address(_defaultPool), _coll);
    }

    function closeTrove(address _borrower) external override {
        _requireCallerIsBorrowerOperations();
        return _closeTrove(_borrower, Status.closedByOwner);
    }

    function _closeTrove(address _borrower, Status closedStatus) internal {
        assert(closedStatus != Status.nonExistent && closedStatus != Status.active);

        uint256 TroveOwnersArrayLength = TroveOwners.length;
        if (thusdToken.mintList(borrowerOperationsAddress)) {
          _requireMoreThanOneTroveInSystem(TroveOwnersArrayLength);
        }

        Troves[_borrower].status = closedStatus;
        Troves[_borrower].coll = 0;
        Troves[_borrower].debt = 0;

        rewardSnapshots[_borrower].collateral = 0;
        rewardSnapshots[_borrower].THUSDDebt = 0;

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

    /*
    * Updates snapshots of system total stakes and total collateral, excluding a given collateral remainder from the calculation.
    * Used in a liquidation sequence.
    *
    * The calculation excludes a portion of collateral that is in the ActivePool:
    *
    * the total collateral gas compensation from the liquidation sequence
    *
    * The collateral as compensation must be excluded as it is always sent out at the very end of the liquidation sequence.
    */
    function _updateSystemSnapshots_excludeCollRemainder(IActivePool _activePool, uint256 _collRemainder) internal {
        totalStakesSnapshot = totalStakes;

        uint256 activeColl = _activePool.getCollateralBalance();
        uint256 liquidatedColl = defaultPool.getCollateralBalance();
        totalCollateralSnapshot = activeColl - _collRemainder + liquidatedColl;

        emit SystemSnapshotsUpdated(totalStakesSnapshot, totalCollateralSnapshot);
    }

    // Push the owner's address to the Trove owners list, and record the corresponding array index on the Trove struct
    function addTroveOwnerToArray(address _borrower) external override returns (uint256 index) {
        _requireCallerIsBorrowerOperations();
        return _addTroveOwnerToArray(_borrower);
    }

    function _addTroveOwnerToArray(address _borrower) internal returns (uint128 index) {
        /* Max array size is 2**128 - 1, i.e. ~3e30 troves. No risk of overflow, since troves have minimum THUSD
        debt of liquidation reserve plus MIN_NET_DEBT. 3e30 THUSD dwarfs the value of all wealth in the world ( which is < 1e15 USD). */

        // Push the Troveowner to the array
        TroveOwners.push(_borrower);

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

        return index;
    }

    /*
    * Remove a Trove owner from the TroveOwners array, not preserving array order. Removing owner 'B' does the following:
    * [A B C D E] => [A E C D], and updates E's Trove struct to point to its new array index.
    */
    function _removeTroveOwner(address _borrower, uint256 TroveOwnersArrayLength) internal {
        Status troveStatus = Troves[_borrower].status;
        // It’s set in caller function `_closeTrove`
        assert(troveStatus != Status.nonExistent && troveStatus != Status.active);

        uint128 index = Troves[_borrower].arrayIndex;
        uint256 length = TroveOwnersArrayLength;
        uint256 idxLast = length - 1;

        assert(index <= idxLast);

        address addressToMove = TroveOwners[idxLast];

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

        TroveOwners.pop();
    }

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

    function getTCR(uint256 _price) external view override returns (uint) {
        return _getTCR(_price);
    }

    function checkRecoveryMode(uint256 _price) external view override returns (bool) {
        return _checkRecoveryMode(_price);
    }

    // Check whether or not the system *would be* in Recovery Mode, given an collateral:USD price, and the entire system coll and debt.
    function _checkPotentialRecoveryMode(
        uint256 _entireSystemColl,
        uint256 _entireSystemDebt,
        uint256 _price
    )
        internal
        pure
    returns (bool)
    {
        uint256 TCR = LiquityMath._computeCR(_entireSystemColl, _entireSystemDebt, _price);

        return TCR < CCR;
    }

    // --- Redemption fee functions ---

    /*
    * This function has two impacts on the baseRate state variable:
    * 1) decays the baseRate based on time passed since last redemption or THUSD borrowing operation.
    * then,
    * 2) increases the baseRate based on the amount redeemed, as a proportion of total debt
    */
    function _updateBaseRateFromRedemption(uint256 _collateralDrawn,  uint256 _price, uint256 _totalTHUSDDebt) internal returns (uint) {
        uint256 decayedBaseRate = _calcDecayedBaseRate();

        /* Convert the drawn collateral back to THUSD at face value rate (1 THUSD:1 USD), in order to get
        * the fraction of total supply that was redeemed at face value. */
        uint256 redeemedTHUSDFraction = _collateralDrawn * _price / _totalTHUSDDebt;

        uint256 newBaseRate = decayedBaseRate + (redeemedTHUSDFraction / BETA);
        newBaseRate = LiquityMath._min(newBaseRate, DECIMAL_PRECISION); // cap baseRate at a maximum of 100%
        //assert(newBaseRate <= DECIMAL_PRECISION); // This is already enforced in the line above
        assert(newBaseRate > 0); // Base rate is always non-zero after redemption

        // Update the baseRate state variable
        baseRate = newBaseRate;
        emit BaseRateUpdated(newBaseRate);

        _updateLastFeeOpTime();

        return newBaseRate;
    }

    function getRedemptionRate() public view override returns (uint) {
        return _calcRedemptionRate(baseRate);
    }

    function getRedemptionRateWithDecay() public view override returns (uint) {
        return _calcRedemptionRate(_calcDecayedBaseRate());
    }

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

    function _getRedemptionFee(uint256 _collateralDrawn) internal view returns (uint) {
        return _calcRedemptionFee(getRedemptionRate(), _collateralDrawn);
    }

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

    function _calcRedemptionFee(uint256 _redemptionRate, uint256 _collateralDrawn) internal pure returns (uint) {
        uint256 redemptionFee = _redemptionRate * _collateralDrawn / DECIMAL_PRECISION;
        require(redemptionFee < _collateralDrawn, "TroveManager: Fee would eat up all returned collateral");
        return redemptionFee;
    }

    // --- Borrowing fee functions ---

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

    function getBorrowingRateWithDecay() public view override returns (uint) {
        return _calcBorrowingRate(_calcDecayedBaseRate());
    }

    function _calcBorrowingRate(uint256 _baseRate) internal pure returns (uint) {
        return LiquityMath._min(
            BORROWING_FEE_FLOOR + _baseRate,
            MAX_BORROWING_FEE
        );
    }

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

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

    function _calcBorrowingFee(uint256 _borrowingRate, uint256 _THUSDDebt) internal pure returns (uint) {
        return _borrowingRate * _THUSDDebt / DECIMAL_PRECISION;
    }


    // Updates the baseRate state variable based on time elapsed since the last redemption or THUSD borrowing operation.
    function decayBaseRateFromBorrowing() external override {
        _requireCallerIsBorrowerOperations();

        uint256 decayedBaseRate = _calcDecayedBaseRate();
        assert(decayedBaseRate <= DECIMAL_PRECISION);  // The baseRate can decay to 0

        baseRate = decayedBaseRate;
        emit BaseRateUpdated(decayedBaseRate);

        _updateLastFeeOpTime();
    }

    // --- Internal fee functions ---

    // Update the last fee operation time only if time passed >= decay interval. This prevents base rate griefing.
    function _updateLastFeeOpTime() internal {
        uint256 timePassed = block.timestamp - lastFeeOperationTime;

        if (timePassed >= 1 minutes) {
            lastFeeOperationTime = block.timestamp;
            emit LastFeeOpTimeUpdated(block.timestamp);
        }
    }

    function _calcDecayedBaseRate() internal view returns (uint) {
        uint256 minutesPassed = _minutesPassedSinceLastFeeOp();
        uint256 decayFactor = LiquityMath._decPow(MINUTE_DECAY_FACTOR, minutesPassed);

        return baseRate * decayFactor / DECIMAL_PRECISION;
    }

    function _minutesPassedSinceLastFeeOp() internal view returns (uint) {
        return (block.timestamp - lastFeeOperationTime) / 1 minutes;
    }

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

    function _requireCallerIsBorrowerOperations() internal view {
        require(msg.sender == borrowerOperationsAddress, "TroveManager: Caller is not the BorrowerOperations contract");
    }

    function _requireTroveIsActive(address _borrower) internal view {
        require(Troves[_borrower].status == Status.active, "TroveManager: Trove does not exist or is closed");
    }

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

    function _requireMoreThanOneTroveInSystem(uint256 TroveOwnersArrayLength) internal view {
        require (TroveOwnersArrayLength > 1 && sortedTroves.getSize() > 1, "TroveManager: Only one trove in the system");
    }

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

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

    function _requireValidMaxFeePercentage(uint256 _maxFeePercentage) internal pure {
        require(_maxFeePercentage >= REDEMPTION_FEE_FLOOR && _maxFeePercentage <= DECIMAL_PRECISION,
            "Max fee percentage must be between 0.5% and 100%");
    }

    // --- Trove property getters ---

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

    function getTroveStake(address _borrower) external view override returns (uint) {
        return Troves[_borrower].stake;
    }

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

    function getTroveColl(address _borrower) external view override returns (uint) {
        return Troves[_borrower].coll;
    }

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

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

    function increaseTroveColl(address _borrower, uint256 _collIncrease) external override returns (uint) {
        _requireCallerIsBorrowerOperations();
        uint256 newColl = Troves[_borrower].coll + _collIncrease;
        Troves[_borrower].coll = newColl;
        return newColl;
    }

    function decreaseTroveColl(address _borrower, uint256 _collDecrease) external override returns (uint) {
        _requireCallerIsBorrowerOperations();
        uint256 newColl = Troves[_borrower].coll - _collDecrease;
        Troves[_borrower].coll = newColl;
        return newColl;
    }

    function increaseTroveDebt(address _borrower, uint256 _debtIncrease) external override returns (uint) {
        _requireCallerIsBorrowerOperations();
        uint256 newDebt = Troves[_borrower].debt + _debtIncrease;
        Troves[_borrower].debt = newDebt;
        return newDebt;
    }

    function decreaseTroveDebt(address _borrower, uint256 _debtDecrease) external override returns (uint) {
        _requireCallerIsBorrowerOperations();
        uint256 newDebt = Troves[_borrower].debt - _debtDecrease;
        Troves[_borrower].debt = newDebt;
        return newDebt;
    }
}

File 2 of 21 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 3 of 21 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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);

    /**
     * @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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}

File 4 of 21 : BaseMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;


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

File 5 of 21 : CheckContract.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;


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 6 of 21 : IERC2612.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

/**
 * @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 7 of 21 : LiquityBase.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "./BaseMath.sol";
import "./LiquityMath.sol";
import "../Interfaces/IActivePool.sol";
import "../Interfaces/IDefaultPool.sol";
import "../Interfaces/IPriceFeed.sol";
import "../Interfaces/ILiquityBase.sol";

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

    uint256 constant public _100pct = 1e18; // 1e18 == 100%

    // Minimum collateral ratio for individual troves
    uint256 constant public MCR = 1.1e18; // 110%

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

    // Amount of THUSD to be locked in gas pool on opening troves
    uint256 constant public THUSD_GAS_COMPENSATION = 200e18;

    // Minimum amount of net THUSD debt a trove must have
    uint256 constant public MIN_NET_DEBT = 1800e18;
    // uint256 constant public MIN_NET_DEBT = 0;

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

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

    IActivePool public activePool;

    IDefaultPool public defaultPool;

    IPriceFeed public override priceFeed;

    // --- Gas compensation functions ---

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

    function _getNetDebt(uint256 _debt) internal pure returns (uint) {
        return _debt - THUSD_GAS_COMPENSATION;
    }

    // Return the amount of collateral to be drawn from a trove's collateral and sent as gas compensation.
    function _getCollGasCompensation(uint256 _entireColl) internal pure returns (uint) {
        return _entireColl / PERCENT_DIVISOR;
    }

    function getEntireSystemColl() public view returns (uint256 entireSystemColl) {
        uint256 activeColl = activePool.getCollateralBalance();
        uint256 liquidatedColl = defaultPool.getCollateralBalance();

        return activeColl + liquidatedColl;
    }

    function getEntireSystemDebt() public view returns (uint256 entireSystemDebt) {
        uint256 activeDebt = activePool.getTHUSDDebt();
        uint256 closedDebt = defaultPool.getTHUSDDebt();

        return activeDebt + closedDebt;
    }

    function _getTCR(uint256 _price) internal view returns (uint256 TCR) {
        uint256 entireSystemColl = getEntireSystemColl();
        uint256 entireSystemDebt = getEntireSystemDebt();

        TCR = LiquityMath._computeCR(entireSystemColl, entireSystemDebt, _price);
        return TCR;
    }

    function _checkRecoveryMode(uint256 _price) internal view returns (bool) {
        uint256 TCR = _getTCR(_price);
        return TCR < CCR;
    }

    function _requireUserAcceptsFee(uint256 _fee, uint256 _amount, uint256 _maxFeePercentage) internal pure {
        uint256 feePercentage = _fee * DECIMAL_PRECISION / _amount;
        require(feePercentage <= _maxFeePercentage, "Fee exceeded provided maximum");
    }
}

File 8 of 21 : LiquityMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

library LiquityMath {

    uint256 internal constant DECIMAL_PRECISION = 1e18;

    /* Precision for Nominal ICR (independent of price). Rationale for the value:
     *
     * - Making it “too high” could lead to overflows.
     * - Making it “too low” could lead to an ICR equal to zero, due to truncation from Solidity floor division.
     *
     * This value of 1e20 is chosen for safety: the NICR will only overflow for numerator > ~1e39 ETH,
     * and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator.
     *
     */
    uint256 internal constant NICR_PRECISION = 1e20;

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

    function _max(uint256 _a, uint256 _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(uint256 x, uint256 y) internal pure returns (uint256 decProd) {
        uint256 prod_xy = x * y;

        decProd = (prod_xy + (DECIMAL_PRECISION / 2)) / 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 one function that represent time in units of minutes:
    * 1) TroveManager._calcDecayedBaseRate
    *
    * 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(uint256 _base, uint256 _minutes) internal pure returns (uint) {

        if (_minutes > 525600000) {_minutes = 525600000;}  // cap to avoid overflow

        if (_minutes == 0) {return DECIMAL_PRECISION;}

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

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

        return decMul(x, y);
  }

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

    function _computeNominalCR(uint256 _coll, uint256 _debt) internal pure returns (uint) {
        if (_debt > 0) {
            return _coll * NICR_PRECISION / _debt;
        }
        // Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR.
        else { // if (_debt == 0)
            return type(uint256).max;
        }
    }

    function _computeCR(uint256 _coll, uint256 _debt, uint256 _price) internal pure returns (uint) {
        if (_debt > 0) {
            uint256 newCollRatio = _coll * _price / _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 type(uint256).max;
        }
    }
}

File 9 of 21 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

/**
 * 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 () {
        _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);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 10 of 21 : IActivePool.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "./IPool.sol";


interface IActivePool is IPool {
    // --- Events ---
    event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
    event TroveManagerAddressChanged(address _newTroveManagerAddress);
    event ActivePoolTHUSDDebtUpdated(uint256 _THUSDDebt);
    event ActivePoolCollateralBalanceUpdated(uint256 _collateral);
    event CollateralAddressChanged(address _newCollateralAddress);
    event CollSurplusPoolAddressChanged(address _newCollSurplusPoolAddress);

    // --- Functions ---
    function sendCollateral(address _account, uint256 _amount) external;
    function updateCollateralBalance(uint256 _amount) external;
    function collateralAddress() external view returns(address);
}

File 11 of 21 : ICollSurplusPool.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;


interface ICollSurplusPool {

    // --- Events ---

    event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
    event TroveManagerAddressChanged(address _newTroveManagerAddress);
    event ActivePoolAddressChanged(address _newActivePoolAddress);
    event CollateralAddressChanged(address _newCollateralAddress);

    event CollBalanceUpdated(address indexed _account, uint256 _newBalance);
    event CollateralSent(address _to, uint256 _amount);

    // --- Contract setters ---

    function setAddresses(
        address _borrowerOperationsAddress,
        address _troveManagerAddress,
        address _activePoolAddress,
        address _collateralAddress
    ) external;

    function getCollateralBalance() external view returns (uint);

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

    function accountSurplus(address _account, uint256 _amount) external;

    function claimColl(address _account) external;

    function updateCollateralBalance(uint256 _amount) external;
    
    function collateralAddress() external view returns(address);
}

File 12 of 21 : IDefaultPool.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "./IPool.sol";


interface IDefaultPool is IPool {
    // --- Events ---
    event TroveManagerAddressChanged(address _newTroveManagerAddress);
    event DefaultPoolTHUSDDebtUpdated(uint256 _THUSDDebt);
    event DefaultPoolCollateralBalanceUpdated(uint256 _collateral);
    event CollateralAddressChanged(address _newCollateralAddress);

    // --- Functions ---
    function sendCollateralToActivePool(uint256 _amount) external;
    function updateCollateralBalance(uint256 _amount) external;
    function collateralAddress() external view returns(address);
}

File 13 of 21 : IGasPool.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;


interface IGasPool {
    // --- Events ---
    event TroveManagerAddressChanged(address _newTroveManagerAddress);
    event THUSDTokenAddressChanged(address _thusdTokenAddress);

    // --- Functions ---
    function sendTHUSD(address _account, uint256 _amount) external;
}

File 14 of 21 : ILiquityBase.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "./IPriceFeed.sol";


interface ILiquityBase {
    function priceFeed() external view returns (IPriceFeed);
}

File 15 of 21 : IPCV.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ITHUSDToken.sol";

interface IPCV {

    // --- Events --
    event THUSDTokenAddressSet(address _thusdTokenAddress);
    event BorrowerOperationsAddressSet(address _borrowerOperationsAddress);
    event CollateralAddressSet(address _collateralAddress);
    event BAMMAddressSet(address _bammAddress);
    event RolesSet(address _council, address _treasury);

    event BAMMDeposit(uint256 _thusdAmount);
    event BAMMWithdraw(uint256 _numShares);
    event THUSDWithdraw(address _recipient, uint256 _thusdAmount);
    event CollateralWithdraw(address _recipient, uint256 _collateralAmount);

    event PCVDebtPaid(uint256 _paidDebt);
    
    event RecipientAdded(address _recipient);
    event RecipientRemoved(address _recipient);

    // --- Functions ---

    function debtToPay() external returns(uint256);
    function payDebt(uint256 _thusdToBurn) external;

    function setAddresses(
        address _thusdTokenAddress, 
        address _borrowerOperations, 
        address payable _bammAddress,
        address _collateralERC20
    ) external;
    function initialize() external;

    function depositToBAMM(uint256 _thusdAmount) external;
    function withdrawFromBAMM(uint256 _numShares) external;
    function withdrawTHUSD(address _recipient, uint256 _thusdAmount) external;
    function withdrawCollateral(address _recipient, uint256 _collateralAmount) external;

    function addRecipientToWhitelist(address _recipient) external;
    function addRecipientsToWhitelist(address[] calldata _recipients) external;
    function removeRecipientFromWhitelist(address _recipient) external;
    function removeRecipientsFromWhitelist(address[] calldata _recipients) external;

    function startChangingRoles(address _council, address _treasury) external;
    function cancelChangingRoles() external;
    function finalizeChangingRoles() external;
    
    function collateralERC20() external view returns(IERC20);
    function thusdToken() external view returns(ITHUSDToken);

}

File 16 of 21 : IPool.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

// Common interface for the Pools.
interface IPool {

    // --- Events ---

    event CollateralBalanceUpdated(uint256 _newBalance);
    event THUSDBalanceUpdated(uint256 _newBalance);
    event ActivePoolAddressChanged(address _newActivePoolAddress);
    event DefaultPoolAddressChanged(address _newDefaultPoolAddress);
    event StabilityPoolAddressChanged(address _newStabilityPoolAddress);
    event CollateralSent(address _to, uint256 _amount);

    // --- Functions ---

    function getCollateralBalance() external view returns (uint);

    function getTHUSDDebt() external view returns (uint);

    function increaseTHUSDDebt(uint256 _amount) external;

    function decreaseTHUSDDebt(uint256 _amount) external;
}

File 17 of 21 : IPriceFeed.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

interface IPriceFeed {

    // --- Events ---
    event LastGoodPriceUpdated(uint256 _lastGoodPrice);
   
    // --- Function ---
    function fetchPrice() external returns (uint);
}

File 18 of 21 : ISortedTroves.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

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

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

    // --- Functions ---
    
    function setParams(uint256 _size, address _TroveManagerAddress, address _borrowerOperationsAddress) 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 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 19 of 21 : IStabilityPool.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

/*
 * The Stability Pool holds THUSD tokens deposited by Stability Pool depositors.
 *
 * When a trove is liquidated, then depending on system conditions, some of its THUSD debt gets offset with
 * THUSD in the Stability Pool:  that is, the offset debt evaporates, and an equal amount of THUSD tokens in the Stability Pool is burned.
 *
 * Thus, a liquidation causes each depositor to receive a THUSD loss, in proportion to their deposit as a share of total deposits.
 * They also receive an collateral gain, as the 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 THUSD 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 / collateral gain derivations:
 * https://github.com/liquity/liquity/blob/master/papers/Scalable_Reward_Distribution_with_Compounding_Stakes.pdf
 *
 */
interface IStabilityPool {

    // --- Events ---

    event StabilityPoolCollateralBalanceUpdated(uint256 _newBalance);
    event StabilityPoolTHUSDBalanceUpdated(uint256 _newBalance);

    event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
    event TroveManagerAddressChanged(address _newTroveManagerAddress);
    event ActivePoolAddressChanged(address _newActivePoolAddress);
    event DefaultPoolAddressChanged(address _newDefaultPoolAddress);
    event THUSDTokenAddressChanged(address _newTHUSDTokenAddress);
    event SortedTrovesAddressChanged(address _newSortedTrovesAddress);
    event PriceFeedAddressChanged(address _newPriceFeedAddress);
    event CollateralAddressChanged(address _newCollateralAddress);

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

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

    event CollateralGainWithdrawn(address indexed _depositor, uint256 _collateral, uint256 _THUSDLoss);
    event CollateralSent(address _to, uint256 _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 _thusdTokenAddress,
        address _sortedTrovesAddress,
        address _priceFeedAddress,
        address _collateralAddress
    ) external;

    /*
     * Initial checks:
     * - _amount is not zero
     * ---
     * - Sends depositor's accumulated gains (collateral) to depositor
     */
    function provideToSP(uint256 _amount) external;

    /*
     * Initial checks:
     * - _amount is zero or there are no under collateralized troves left in the system
     * - User has a non zero deposit
     * ---
     * - Sends all depositor's accumulated gains (collateral) to depositor
     * - Decreases deposit stake, and takes new snapshot.
     *
     * If _amount > userDeposit, the user withdraws all of their compounded deposit.
     */
    function withdrawFromSP(uint256 _amount) external;

    /*
     * Initial checks:
     * - User has a non zero deposit
     * - User has an open trove
     * - User has some collateral gain
     * ---
     * - Transfers the depositor's entire collateral gain from the Stability Pool to the caller's trove
     * - Leaves their compounded deposit in the Stability Pool
     * - Updates snapshots for deposit
     */
    function withdrawCollateralGainToTrove(address _upperHint, address _lowerHint) external;

    /*
     * Initial checks:
     * - Caller is TroveManager
     * ---
     * Cancels out the specified debt against the THUSD contained in the Stability Pool (as far as possible)
     * and transfers the Trove's collateral from ActivePool to StabilityPool.
     * Only called by liquidation functions in the TroveManager.
     */
    function offset(uint256 _debt, uint256 _coll) external;

    /*
     * Returns the total amount of collateral held by the pool, accounted in an internal variable instead of `balance`,
     * to exclude edge cases like collateral received from a self-destruct.
     */
    function getCollateralBalance() external view returns (uint);

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

    /*
     * Calculates the collateral gain earned by the deposit since its last snapshots were taken.
     */
    function getDepositorCollateralGain(address _depositor) external view returns (uint);

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

    /*
     * Only callable by Active Pool, updates ERC20 tokens recieved
     */
    function updateCollateralBalance(uint256 _amount) external;
    /*
     * Fallback function
     * Only callable by Active Pool, it just accounts for ETH received
     * receive() external payable;
     */
    
    function collateralAddress() external view returns(address);
}

File 20 of 21 : ITHUSDToken.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../Dependencies/IERC2612.sol";

interface ITHUSDToken is IERC20Metadata, IERC2612 {

    // --- Events ---

    event TroveManagerAddressAdded(address _troveManagerAddress);
    event StabilityPoolAddressAdded(address _newStabilityPoolAddress);
    event BorrowerOperationsAddressAdded(address _newBorrowerOperationsAddress);

    event THUSDTokenBalanceUpdated(address _user, uint256 _amount);

    // --- Functions ---
    function mintList(address contractAddress) external view returns (bool);
    function burnList(address contractAddress) external view returns (bool);

    function mint(address _account, uint256 _amount) external;

    function burn(address _account, uint256 _amount) external;

    function increaseAllowance(address spender, uint256 addedValue) external returns (bool);

    function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
}

File 21 of 21 : ITroveManager.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "./ILiquityBase.sol";
import "./IStabilityPool.sol";
import "./ITHUSDToken.sol";
import "./IPCV.sol";


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

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

    // --- Events ---

    event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
    event PriceFeedAddressChanged(address _newPriceFeedAddress);
    event THUSDTokenAddressChanged(address _newTHUSDTokenAddress);
    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 PCVAddressChanged(address _pcvAddress);

    event Liquidation(uint256 _liquidatedDebt, uint256 _liquidatedColl, uint256 _collGasCompensation, uint256 _THUSDGasCompensation);
    event Redemption(uint256 _attemptedTHUSDAmount, uint256 _actualTHUSDAmount, uint256 _collateralSent, uint256 _collateralFee);
    event TroveUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 stake, uint8 operation);
    event TroveLiquidated(address indexed _borrower, uint256 _debt, uint256 _coll, uint8 operation);
    event BaseRateUpdated(uint256 _baseRate);
    event LastFeeOpTimeUpdated(uint256 _lastFeeOpTime);
    event TotalStakesUpdated(uint256 _newTotalStakes);
    event SystemSnapshotsUpdated(uint256 _totalStakesSnapshot, uint256 _totalCollateralSnapshot);
    event LTermsUpdated(uint256 _L_Collateral, uint256 _L_THUSDDebt);
    event TroveSnapshotsUpdated(uint256 _L_Collateral, uint256 _L_THUSDDebt);
    event TroveIndexUpdated(address _borrower, uint256 _newIndex);

    // --- Functions ---

    function setAddresses(
        address _borrowerOperationsAddress,
        address _activePoolAddress,
        address _defaultPoolAddress,
        address _stabilityPoolAddress,
        address _gasPoolAddress,
        address _collSurplusPoolAddress,
        address _priceFeedAddress,
        address _thusdTokenAddress,
        address _sortedTrovesAddress,
        address _pcvAddress
    ) external;

    function stabilityPool() external view returns (IStabilityPool);
    function thusdToken() external view returns (ITHUSDToken);
    function pcv() external view returns (IPCV);

    function getTroveOwnersCount() external view returns (uint);

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

    function getNominalICR(address _borrower) external view returns (uint);
    function getCurrentICR(address _borrower, uint256 _price) external view returns (uint);

    function liquidate(address _borrower) external;

    function liquidateTroves(uint256 _n) external;

    function batchLiquidateTroves(address[] calldata _troveArray) external;

    function redeemCollateral(
        uint256 _THUSDAmount,
        address _firstRedemptionHint,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintNICR,
        uint256 _maxIterations,
        uint256 _maxFee
    ) external;

    function updateStakeAndTotalStakes(address _borrower) external returns (uint);

    function updateTroveRewardSnapshots(address _borrower) external;

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

    function applyPendingRewards(address _borrower) external;

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

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

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

    function getEntireDebtAndColl(address _borrower) external view returns (
        uint256 debt,
        uint256 coll,
        uint256 pendingTHUSDDebtReward,
        uint256 pendingCollateralReward
    );

    function closeTrove(address _borrower) external;

    function removeStake(address _borrower) external;

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

    function getRedemptionFeeWithDecay(uint256 _collateralDrawn) external view returns (uint);

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

    function getBorrowingFee(uint256 THUSDDebt) external view returns (uint);
    function getBorrowingFeeWithDecay(uint256 _THUSDDebt) external view returns (uint);

    function decayBaseRateFromBorrowing() external;

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

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

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

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

    function setTroveStatus(address _borrower, Status _status) external;

    function increaseTroveColl(address _borrower, uint256 _collIncrease) external returns (uint);

    function decreaseTroveColl(address _borrower, uint256 _collDecrease) external returns (uint);

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

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

    function getTCR(uint256 _price) external view returns (uint);

    function checkRecoveryMode(uint256 _price) external view returns (bool);
}

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

Contract Security Audit

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_activePoolAddress","type":"address"}],"name":"ActivePoolAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_baseRate","type":"uint256"}],"name":"BaseRateUpdated","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":false,"internalType":"uint256","name":"_L_Collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_L_THUSDDebt","type":"uint256"}],"name":"LTermsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_lastFeeOpTime","type":"uint256"}],"name":"LastFeeOpTimeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_liquidatedDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_liquidatedColl","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collGasCompensation","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_THUSDGasCompensation","type":"uint256"}],"name":"Liquidation","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":"address","name":"_pcvAddress","type":"address"}],"name":"PCVAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newPriceFeedAddress","type":"address"}],"name":"PriceFeedAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_attemptedTHUSDAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_actualTHUSDAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collateralSent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collateralFee","type":"uint256"}],"name":"Redemption","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":false,"internalType":"uint256","name":"_totalStakesSnapshot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_totalCollateralSnapshot","type":"uint256"}],"name":"SystemSnapshotsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newTHUSDTokenAddress","type":"address"}],"name":"THUSDTokenAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_newTotalStakes","type":"uint256"}],"name":"TotalStakesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_newIndex","type":"uint256"}],"name":"TroveIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_debt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_coll","type":"uint256"},{"indexed":false,"internalType":"enum TroveManager.TroveManagerOperation","name":"_operation","type":"uint8"}],"name":"TroveLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_L_Collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_L_THUSDDebt","type":"uint256"}],"name":"TroveSnapshotsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_debt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_coll","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_stake","type":"uint256"},{"indexed":false,"internalType":"enum TroveManager.TroveManagerOperation","name":"_operation","type":"uint8"}],"name":"TroveUpdated","type":"event"},{"inputs":[],"name":"BETA","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":"L_Collateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"L_THUSDDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BORROWING_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINUTE_DECAY_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_NET_DEBT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"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":"THUSD_GAS_COMPENSATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"TroveOwners","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"Troves","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"enum ITroveManager.Status","name":"status","type":"uint8"},{"internalType":"uint128","name":"arrayIndex","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_100pct","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":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"addTroveOwnerToArray","outputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"applyPendingRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_troveArray","type":"address[]"}],"name":"batchLiquidateTroves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowerOperationsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"checkRecoveryMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"closeTrove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decayBaseRateFromBorrowing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_collDecrease","type":"uint256"}],"name":"decreaseTroveColl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_debtDecrease","type":"uint256"}],"name":"decreaseTroveDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultPool","outputs":[{"internalType":"contract IDefaultPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_THUSDDebt","type":"uint256"}],"name":"getBorrowingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_THUSDDebt","type":"uint256"}],"name":"getBorrowingFeeWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBorrowingRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBorrowingRateWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"getCurrentICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getEntireDebtAndColl","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"pendingTHUSDDebtReward","type":"uint256"},{"internalType":"uint256","name":"pendingCollateralReward","type":"uint256"}],"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":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getNominalICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPendingCollateralReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPendingTHUSDDebtReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralDrawn","type":"uint256"}],"name":"getRedemptionFeeWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedemptionRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedemptionRateWithDecay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"getTCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getTroveColl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getTroveDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getTroveFromTroveOwnersArray","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTroveOwnersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getTroveStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getTroveStatus","outputs":[{"internalType":"enum ITroveManager.Status","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"hasPendingRewards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_collIncrease","type":"uint256"}],"name":"increaseTroveColl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_debtIncrease","type":"uint256"}],"name":"increaseTroveDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastCollateralError_Redistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastFeeOperationTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastTHUSDDebtError_Redistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_n","type":"uint256"}],"name":"liquidateTroves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pcv","outputs":[{"internalType":"contract IPCV","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeed","outputs":[{"internalType":"contract IPriceFeed","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_THUSDamount","type":"uint256"},{"internalType":"address","name":"_firstRedemptionHint","type":"address"},{"internalType":"address","name":"_upperPartialRedemptionHint","type":"address"},{"internalType":"address","name":"_lowerPartialRedemptionHint","type":"address"},{"internalType":"uint256","name":"_partialRedemptionHintNICR","type":"uint256"},{"internalType":"uint256","name":"_maxIterations","type":"uint256"},{"internalType":"uint256","name":"_maxFeePercentage","type":"uint256"}],"name":"redeemCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"removeStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardSnapshots","outputs":[{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"THUSDDebt","type":"uint256"}],"stateMutability":"view","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":"_priceFeedAddress","type":"address"},{"internalType":"address","name":"_thusdTokenAddress","type":"address"},{"internalType":"address","name":"_sortedTrovesAddress","type":"address"},{"internalType":"address","name":"_pcvAddress","type":"address"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"enum ITroveManager.Status","name":"_status","type":"uint8"}],"name":"setTroveStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sortedTroves","outputs":[{"internalType":"contract ISortedTroves","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stabilityPool","outputs":[{"internalType":"contract IStabilityPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"thusdToken","outputs":[{"internalType":"contract ITHUSDToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateralSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakesSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"updateStakeAndTotalStakes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"updateTroveRewardSnapshots","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50600380546001600160a01b031916339081179091556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3615461806100606000396000f3fe608060405234801561001057600080fd5b50600436106104145760003560e01c8063795d26c311610221578063bcd375261161012b578063d5b35635116100c3578063ee266b8711610087578063ee266b87146109c5578063f2fde38b146109ce578063f36b2425146109e1578063f92d343314610562578063fe2ba848146109e957600080fd5b8063d5b356351461095a578063d66a25531461096d578063d9a7244414610996578063e2ac77b0146109a9578063e6a91f16146109bc57600080fd5b8063bcd37526146108dc578063bf9befb1146108ef578063c52861f2146108f8578063c7b5548114610900578063cbd138ae1461090f578063d293c71014610922578063d380a37c14610935578063d38b05581461093e578063d3d6f8431461094757600080fd5b80639976cf45116101b9578063ae9187541161017d578063ae9187541461085d578063b0d8e18114610870578063b7f8cf9b14610883578063b82f263d14610896578063b91af97c146108a957600080fd5b80639976cf45146107f65780639ba7a40814610809578063a20baee614610720578063a3f4df7e1461081c578063a68b663e1461085457600080fd5b8063795d26c3146107775780637f7dde4a1461077f578063807d138d1461079257806382fe3eb91461079b578063887105d3146107ae5780638da5cb5b146107b65780638f32d59b146107c7578063953f0bb1146107da57806396d711ff146107ed57600080fd5b8063480cd57811610322578063653d46e7116102ba57806372fe25aa1161027e57806372fe25aa14610720578063741bef1a1461072f578063756b253e1461074257806377e16f1e14610755578063794e57241461076857600080fd5b8063653d46e71461068657806366ca4a21146106995780636c37a4af146106a15780636ef64338146106b457806372423c171461070d57600080fd5b8063480cd578146105be5780634870dd9a146105ea57806349eefeee146105f25780634a3c95c4146105fa5780634e443d9e1461060d5780635733d58f146106305780635dba4c4a1461063f578063631203b01461064757806364cee2601461065a57600080fd5b80631f68f20a116103b05780632b11551a116103745780632b11551a1461056a5780632f865568146105725780633cc74225146105855780634597f6ed14610598578063477d66cf146105ab57600080fd5b80631f68f20a146105055780631fd6a4341461050e57806321e378011461051e578063240926691461055a57806328d28b5b1461056257600080fd5b8063048c661d14610419578063071a7541146104425780630b0765571461045857806312610e921461046d57806315d549f1146104805780631673c79a1461049357806318f2817a146104cf5780631bf43555146104e25780631e8b1c2b146104f2575b600080fd5b60055461042c906001600160a01b031681565b6040516104399190614e03565b60405180910390f35b61044a600281565b604051908152602001610439565b61046b610466366004614e3c565b6109fc565b005b61044a61047b366004614e59565b610a24565b61044a61048e366004614e3c565b610a77565b6104ba6104a1366004614e3c565b6013602052600090815260409020805460019091015482565b60408051928352602083019190915201610439565b61044a6104dd366004614e3c565b610a99565b61044a686194049f30f720000081565b61046b610500366004614e9b565b610aac565b61044a600b5481565b61044a680ad78ebc5ac620000081565b61054d61052c366004614e3c565b6001600160a01b03166000908152600d602052604090206003015460ff1690565b6040516104399190614f8a565b61044a610e4d565b61044a610e6e565b61044a610e82565b61046b610580366004614e3c565b610e94565b60015461042c906001600160a01b031681565b60095461042c906001600160a01b031681565b61044a6105b9366004614f98565b610f00565b61044a6105cc366004614e3c565b6001600160a01b03166000908152600d602052604090206001015490565b61044a60c881565b60145461044a565b61044a610608366004614e3c565b610f13565b61062061061b366004614f98565b610fce565b6040519015158152602001610439565b61044a6714d1120d7b16000081565b61046b610fd9565b61044a610655366004614f98565b611045565b61044a610668366004614e3c565b6001600160a01b03166000908152600d602052604090206002015490565b61046b610694366004614f98565b611052565b61044a6113c8565b61046b6106af366004614fb1565b6113da565b6106fc6106c2366004614e3c565b600d6020526000908152604090208054600182015460028301546003909301549192909160ff81169061010090046001600160801b031685565b60405161043995949392919061507d565b61044a61071b366004614e59565b61172b565b61044a670de0b6b3a764000081565b60025461042c906001600160a01b031681565b61042c610750366004614f98565b611783565b60085461042c906001600160a01b031681565b61044a670f43fc2c04ee000081565b61044a6117ad565b60005461042c906001600160a01b031681565b61044a600f5481565b61046b6107a9366004614e3c565b6118a9565b61044a6118ba565b6003546001600160a01b031661042c565b6003546001600160a01b03163314610620565b61046b6107e83660046150ba565b61197f565b61044a60105481565b61044a610804366004614e59565b6119c8565b61044a610817366004614e3c565b6119f6565b6108476040518060400160405280600c81526020016b2a3937bb32a6b0b730b3b2b960a11b81525081565b60405161043991906150f7565b61044a60125481565b600a5461042c906001600160a01b031681565b61044a61087e366004614e3c565b611a22565b60045461042c906001600160a01b031681565b61044a6108a4366004614f98565b611a49565b6108bc6108b7366004614e3c565b611a54565b604080519485526020850193909352918301526060820152608001610439565b61046b6108ea366004615145565b611aac565b61044a600e5481565b61044a612148565b61044a670ddd4b8c6c7d70d881565b61046b61091d366004614e3c565b61215a565b61044a610930366004614e59565b61216d565b61044a600c5481565b61044a60155481565b61044a610955366004614e59565b61218c565b61044a610968366004614f98565b6121bd565b61044a61097b366004614e3c565b6001600160a01b03166000908152600d602052604090205490565b61042c6109a4366004614f98565b6121d0565b6106206109b7366004614e3c565b612200565b61044a60165481565b61044a60115481565b61046b6109dc366004614e3c565b612263565b61044a6122fb565b61046b6109f7366004614e3c565b612308565b610a04612319565b600054600154610a21916001600160a01b0390811691168361239b565b50565b6000610a2e612319565b6001600160a01b0383166000908152600d6020526040812054610a529084906151c9565b6001600160a01b0385166000908152600d602052604090208190559150505b92915050565b6000610a81612319565b610a8a82612497565b6001600160801b031692915050565b6000610aa3612319565b610a7182612530565b8051600003610b215760405162461bcd60e51b815260206004820152603660248201527f54726f76654d616e616765723a2043616c6c646174612061646472657373206160448201527572726179206d757374206e6f7420626520656d70747960501b60648201526084015b60405180910390fd5b6000546001546005546001600160a01b03928316929182169116610b43614d3e565b610b4b614d6f565b600260009054906101000a90046001600160a01b03166001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610ba0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc491906151dc565b826000018181525050826001600160a01b03166301081fda6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2f91906151dc565b60208301528151610c3f906125d5565b1580156040840152610c6757610c608585846000015185602001518a6125f2565b9050610c7f565b610c7c8585846000015185602001518a612827565b90505b6000816020015111610ca35760405162461bcd60e51b8152600401610b18906151f5565b608081015160a082015160405163335525ad60e01b81526001600160a01b0386169263335525ad92610ce092600401918252602082015260400190565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d2685858360c001518460e001516128fc565b61010081015115610d9d57600754610100820151604051636250216960e01b81526001600160a01b0388811693636250216993610d6a939290911691600401615237565b600060405180830381600087803b158015610d8457600080fd5b505af1158015610d98573d6000803e3d6000fd5b505050505b610dab858260400151612b3b565b6020810151606083015261010081015160408201518251610dcc91906151c9565b610dd691906151c9565b6080838101829052606080850151604085810151868401518251938452602084019690965290820152908101929092527f4152c73dd2614c4f9fc35e8c9cf16013cd588c75b49a4c1673ecffdcbcda9403910160405180910390a1610e45853383606001518460400151612c86565b505050505050565b610e606064670de0b6b3a7640000615266565b610e6b90600561527a565b81565b610e606103e8670de0b6b3a7640000615266565b6000610e8f600b54612d5d565b905090565b610e9d81612d9a565b604080516001808252818301909252600091602080830190803683370190505090508181600081518110610ed357610ed3615291565b60200260200101906001600160a01b031690816001600160a01b031681525050610efc81610aac565b5050565b6000610a71610f0d6113c8565b83612e31565b6001600160a01b0381166000908152601360205260408120546011548290610f3c9083906151c9565b9050801580610f7b575060016001600160a01b0385166000908152600d602052604090206003015460ff166004811115610f7857610f78614f60565b14155b15610f8a575060009392505050565b6001600160a01b0384166000908152600d602052604081206002015490670de0b6b3a7640000610fba848461527a565b610fc49190615266565b9695505050505050565b6000610a71826125d5565b610fe1612319565b6000610feb612e57565b9050670de0b6b3a7640000811115611005576110056152a7565b600b8190556040518181527fc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600c9060200160405180910390a1610a21612e9b565b6000610a71610f0d6122fb565b6040805160e081018252600080546001600160a01b0390811683526001548116602084015292820181905260608201819052600a548316608083015260a0820181905260c08201526005549091166110a8614d3e565b6110b0614d6f565b600260009054906101000a90046001600160a01b03166001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611105573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112991906151dc565b826000018181525050826001600160a01b03166301081fda6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611170573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119491906151dc565b602083015281516111a4906125d5565b15801560408401526111cb576111c4848360000151846020015188612ef2565b90506111eb565b6111e884600001518560200151846000015185602001518961327e565b90505b600081602001511161120f5760405162461bcd60e51b8152600401610b18906151f5565b608081015160a082015160405163335525ad60e01b81526001600160a01b0386169263335525ad9261124c92600401918252602082015260400190565b600060405180830381600087803b15801561126657600080fd5b505af115801561127a573d6000803e3d6000fd5b5050505061129a846000015185602001518360c001518460e001516128fc565b61010081015115611311578351600754610100830151604051636250216960e01b81526001600160a01b03938416936362502169936112de93911691600401615237565b600060405180830381600087803b1580156112f857600080fd5b505af115801561130c573d6000803e3d6000fd5b505050505b61132384600001518260400151612b3b565b602081015160608301526101008101516040820151825161134491906151c9565b61134e91906151c9565b6080838101829052606080850151604085810151868401518251938452602084019690965290820152908101929092527f4152c73dd2614c4f9fc35e8c9cf16013cd588c75b49a4c1673ecffdcbcda9403910160405180910390a16113c184600001513383606001518460400151612c86565b5050505050565b6000610e8f6113d5612e57565b6133b4565b6003546001600160a01b031633146114045760405162461bcd60e51b8152600401610b18906152bd565b61140d8a613406565b61141689613406565b61141f88613406565b61142887613406565b61143186613406565b61143a85613406565b61144384613406565b61144c83613406565b61145582613406565b61145e81613406565b600480546001600160a01b03199081166001600160a01b038d8116919091179092556000805482168c84161790556001805482168b84161790556005805482168a8416179055600680548216898416179055600780548216888416179055600280548216878416179055600880548216868416179055600a80548216858416179055600980549091169183169190911790556040517f3ca631ffcd2a9b5d9ae18543fc82f58eb4ca33af9e6ab01b7a8e95331e6ed98590611520908c90614e03565b60405180910390a17f78f058b189175430c48dc02699e3a0031ea4ff781536dc2fab847de4babdd882896040516115579190614e03565b60405180910390a17f5ee0cae2f063ed938bb55046f6a932fb6ae792bf43624806bb90abe68a50be9b8860405161158e9190614e03565b60405180910390a17f82966d27eea39b038ee0fa30cd16532bb24f6e65d31cb58fb227aa5766cdcc7f876040516115c59190614e03565b60405180910390a17fcfb07d791fcafc032b35837b50eb84b74df518cf4cc287e8084f47630fa70fa0866040516115fc9190614e03565b60405180910390a17fe67f36a6e961157d6eff83b91f3af5a62131ceb6f04954ef74f51c1c05e7f88d856040516116339190614e03565b60405180910390a17f8c537274438aa850a330284665d81a85dd38267d09e4050d416bfc94142db2648460405161166a9190614e03565b60405180910390a17fe1e858a66c0bbbcdfa22d58dde1e5d42370be20cdb176e560287f85412e546e0836040516116a19190614e03565b60405180910390a17f65f4cf077bc01e4742eb5ad98326f6e95b63548ea24b17f8d5e823111fe78800826040516116d89190614e03565b60405180910390a17f38335c64466e2376ab931166337e19127650d842036ebe01da1ba3e5c1255ebb8160405161170f9190614e03565b60405180910390a161171f6134ab565b50505050505050505050565b6000611735612319565b6001600160a01b0383166000908152600d602052604081206001015461175c9084906152f2565b6001600160a01b0385166000908152600d6020526040902060010181905591505092915050565b6014818154811061179357600080fd5b6000918252602090912001546001600160a01b0316905081565b600080546040805163f07424e960e01b8152905183926001600160a01b03169163f07424e99160048083019260209291908290030181865afa1580156117f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181b91906151dc565b90506000600160009054906101000a90046001600160a01b03166001600160a01b031663f07424e96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611872573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189691906151dc565b90506118a281836152f2565b9250505090565b6118b1612319565b610a21816134f5565b6000805460408051631529a63960e01b8152905183926001600160a01b031691631529a6399160048083019260209291908290030181865afa158015611904573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061192891906151dc565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631529a6396040518163ffffffff1660e01b8152600401602060405180830381865afa158015611872573d6000803e3d6000fd5b611987612319565b6001600160a01b0382166000908152600d60205260409020600301805482919060ff191660018360048111156119bf576119bf614f60565b02179055505050565b60006119d2612319565b6001600160a01b0383166000908152600d6020526040812054610a529084906152f2565b6001600160a01b0381166000908152601360205260408120600101546012548290610f3c9083906151c9565b6000806000611a3084613559565b915091506000611a4083836135d5565b95945050505050565b6000610a718261360b565b6001600160a01b0381166000908152600d602052604081208054600190910154909180611a80856119f6565b9150611a8b85610f13565b9050611a9782856152f2565b9350611aa381846152f2565b92509193509193565b6040805160e08082018352600080546001600160a01b03908116845260015481166020808601919091526008548216858701526009548216606080870191909152600a548316608080880191909152600754841660a08089019190915260065490941660c080890191909152885161010081018a5286815293840186905297830185905290820184905281018390529081018290529384018190529083015290611b5583613637565b600260009054906101000a90046001600160a01b03166001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bce91906151dc565b60c08201819052611bde906136d2565b611be789613745565b611bf68260400151338b6137ac565b611bfe6117ad565b60e0820152888152608082015160c0820151600091611c1e918b906138a7565b15611c2a575087611d3c565b82608001516001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c909190615305565b90505b6001600160a01b03811615801590611cbf5750670f43fc2c04ee0000611cbd828460c0015161216d565b105b15611d3c5782608001516001600160a01b031663b72703ac826040518263ffffffff1660e01b8152600401611cf49190614e03565b602060405180830381865afa158015611d11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d359190615305565b9050611c93565b84600003611d4a5760001994505b6001600160a01b03811615801590611d625750815115155b8015611d6e5750600085115b15611e815784611d7d81615322565b955050600083608001516001600160a01b031663b72703ac836040518263ffffffff1660e01b8152600401611db29190614e03565b602060405180830381865afa158015611dcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df39190615305565b9050611e08846000015185602001518461239b565b6000611e21858486600001518760c001518e8e8e6139f1565b9050806040015115611e34575050611e81565b8051602085018051611e479083906152f2565b9052506020810151604085018051611e609083906152f2565b905250805184518590611e749083906151c9565b905250909150611d4a9050565b6000826040015111611ee75760405162461bcd60e51b815260206004820152602960248201527f54726f76654d616e616765723a20556e61626c6520746f2072656465656d20616044820152681b9e48185b5bdd5b9d60ba1b6064820152608401610b18565b611efe82604001518360c001518460e00151613c7f565b50611f0c8260400151613d22565b606083018190526040830151611f23919086613d2f565b825160608085015190840151604051636250216960e01b81526001600160a01b0390931692636250216992611f5c929091600401615237565b600060405180830381600087803b158015611f7657600080fd5b505af1158015611f8a573d6000803e3d6000fd5b50505060608301516040840151611fa192506151c9565b60808301526020820151604080840151606085015191517f43a3f4082a4dbc33d78e317d2497d3a730bc7fc3574159dcea1056e62e5d9ad893611ffd938f93919293845260208401929092526040830152606082015260800190565b60405180910390a182604001516001600160a01b0316639dc29fac3384602001516040518363ffffffff1660e01b815260040161203b929190615237565b600060405180830381600087803b15801561205557600080fd5b505af1158015612069573d6000803e3d6000fd5b505084516020850151604051637b7fc9eb60e11b81526001600160a01b03909216935063f6ff93d692506120a39160040190815260200190565b600060405180830381600087803b1580156120bd57600080fd5b505af11580156120d1573d6000803e3d6000fd5b505084516080850151604051636250216960e01b81526001600160a01b0390921693506362502169925061210a91339190600401615237565b600060405180830381600087803b15801561212457600080fd5b505af1158015612138573d6000803e3d6000fd5b5050505050505050505050505050565b6000610e8f612155612e57565b612d5d565b612162612319565b610a21816002613da0565b600080600061217b85613559565b915091506000610fc4838387613f47565b6000612196612319565b6001600160a01b0383166000908152600d602052604081206001015461175c9084906151c9565b6000610a716121ca612148565b83613f78565b6000601482815481106121e5576121e5615291565b6000918252602090912001546001600160a01b031692915050565b600060016001600160a01b0383166000908152600d602052604090206003015460ff16600481111561223457612234614f60565b1461224157506000919050565b506011546001600160a01b039091166000908152601360205260409020541090565b6003546001600160a01b0316331461228d5760405162461bcd60e51b8152600401610b18906152bd565b6001600160a01b0381166122f25760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610b18565b610a2181614008565b6000610e8f600b546133b4565b612310612319565b610a218161405a565b6004546001600160a01b031633146123995760405162461bcd60e51b815260206004820152603b60248201527f54726f76654d616e616765723a2043616c6c6572206973206e6f74207468652060448201527f426f72726f7765724f7065726174696f6e7320636f6e747261637400000000006064820152608401610b18565b565b6123a481612200565b15612492576123b281612d9a565b60006123bd82610f13565b905060006123ca836119f6565b6001600160a01b0384166000908152600d60205260408120600101805492935084929091906123fa9084906152f2565b90915550506001600160a01b0383166000908152600d6020526040812080548392906124279084906152f2565b909155506124369050836134f5565b612442858583856140ac565b6001600160a01b0383166000818152600d602052604080822080546001820154600290920154925160008051602061540c833981519152946124879492939291615349565b60405180910390a250505b505050565b601480546001808201835560008381527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec90920180546001600160a01b0319166001600160a01b038616179055915490916124f1916151c9565b6001600160a01b03929092166000908152600d602052604090206003018054610100600160881b0319166101006001600160801b038516021790555090565b6001600160a01b0381166000908152600d602052604081206001015481906125579061418d565b6001600160a01b0384166000908152600d60205260409020600201805490829055600e5491925090829061258c9083906151c9565b61259691906152f2565b600e8190556040519081527f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae5203829060200160405180910390a15092915050565b6000806125e18361360b565b6714d1120d7b160000119392505050565b6125fa614d6f565b612602614dbb565b61260a614d6f565b8482526000608083015261261c6117ad565b60a08301526126296118ba565b60c0830152600060208301525b83518260200151101561281c578382602001518151811061265957612659615291565b6020908102919091018101516001600160a01b0316606084018190526000908152600d909152604090206003015460019060ff16600481111561269e5761269e614f60565b03612804576126b182606001518761216d565b604083015260808201516127a757670f43fc2c04ee00008260400151101580156126da57508151155b6128045760006126f38360c001518460a0015189613f47565b90506127108989856060015186604001518760000151868d6141c3565b915081608001518360000181815161272891906151c9565b905250608082015160a0840180516127419083906151c9565b905250610100820151604083015160a084015161275e91906152f2565b61276891906152f2565b8360c00181815161277991906151c9565b90525061278684836144be565b935061279b8360c001518460a00151896145a8565b15608084015250612804565b816080015180156127c35750670f43fc2c04ee00008260400151105b15612804576127dc8888846060015185600001516145c9565b90508060800151826000018181516127f491906151c9565b90525061280183826144be565b92505b602082018051906128148261536b565b905250612636565b505095945050505050565b61282f614d6f565b612837614dbb565b61283f614d6f565b848252600060208301525b83518260200151101561281c578382602001518151811061286d5761286d615291565b60209081029190910101516001600160a01b031660608301819052612892908761216d565b60408301819052670f43fc2c04ee000011156128e4576128bc8888846060015185600001516145c9565b90508060800151826000018181516128d491906151c9565b9052506128e183826144be565b92505b602082018051906128f48261536b565b90525061284a565b8115612b355760155460009061291a670de0b6b3a76400008461527a565b61292491906152f2565b90506000601654670de0b6b3a76400008561293f919061527a565b61294991906152f2565b90506000600e548361295b9190615266565b90506000600e548361296d9190615266565b9050600e548261297d919061527a565b61298790856151c9565b601555600e54612997908261527a565b6129a190846151c9565b60168190555081601160008282546129b991906152f2565b9250508190555080601260008282546129d291906152f2565b90915550506011546012546040517f9f8bc8ab0daf5bceef75ecfd2085d1fcc6548c657ea970d9a23a60610d0737e392612a1492908252602082015260400190565b60405180910390a1604051637b7fc9eb60e11b8152600481018790526001600160a01b0389169063f6ff93d690602401600060405180830381600087803b158015612a5e57600080fd5b505af1158015612a72573d6000803e3d6000fd5b50506040516342c31a2360e01b8152600481018990526001600160a01b038a1692506342c31a239150602401600060405180830381600087803b158015612ab857600080fd5b505af1158015612acc573d6000803e3d6000fd5b5050604051636250216960e01b81526001600160a01b038b16925063625021699150612afe908a908990600401615237565b600060405180830381600087803b158015612b1857600080fd5b505af1158015612b2c573d6000803e3d6000fd5b50505050505050505b50505050565b600e54600f819055506000826001600160a01b0316631529a6396040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba891906151dc565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631529a6396040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2391906151dc565b905080612c3084846151c9565b612c3a91906152f2565b6010819055600f546040517f51bf4c63ec3cba9d03d43238abbdd979dd91bd16d9895c74ceea9118c7baaf6092612c78928252602082015260400190565b60405180910390a150505050565b8115612cf1576006546040516316268b0d60e21b81526001600160a01b039091169063589a2c3490612cbe9086908690600401615237565b600060405180830381600087803b158015612cd857600080fd5b505af1158015612cec573d6000803e3d6000fd5b505050505b8015612b3557604051636250216960e01b81526001600160a01b03851690636250216990612d259086908590600401615237565b600060405180830381600087803b158015612d3f57600080fd5b505af1158015612d53573d6000803e3d6000fd5b5050505050505050565b6000610a7182612d776103e8670de0b6b3a7640000615266565b612d8290600561527a565b612d8c91906152f2565b670de0b6b3a7640000614710565b60016001600160a01b0382166000908152600d602052604090206003015460ff166004811115612dcc57612dcc614f60565b14610a215760405162461bcd60e51b815260206004820152602f60248201527f54726f76654d616e616765723a2054726f766520646f6573206e6f742065786960448201526e1cdd081bdc881a5cc818db1bdcd959608a1b6064820152608401610b18565b6000670de0b6b3a7640000612e46838561527a565b612e509190615266565b9392505050565b600080612e62614726565b90506000612e78670ddd4b8c6c7d70d883614742565b9050670de0b6b3a764000081600b54612e91919061527a565b6118a29190615266565b6000600c5442612eab91906151c9565b9050603c8110610a215742600c8190556040519081527f860f8d2f0c74dd487e89e2883e3b25b8159ce1e1b3433a291cba7b82c508f3bc906020015b60405180910390a150565b612efa614d6f565b612f02614dbb565b612f0a614d6f565b84825260006080830152612f1c6117ad565b60a0830152612f296118ba565b8260c001818152505086608001516001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f989190615305565b82606001906001600160a01b031690816001600160a01b031681525050600087608001516001600160a01b0316631e2231436040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ff9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061301d9190615305565b6000602085015290505b8483602001511080156130505750806001600160a01b031683606001516001600160a01b031614155b156132735760808801516060840151604051632dc9c0eb60e21b81526000926001600160a01b03169163b72703ac9161308c9190600401614e03565b602060405180830381865afa1580156130a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130cd9190615305565b90506130dd84606001518961216d565b604085015260808401516131de57670f43fc2c04ee000084604001511015801561310657508351155b156131115750613273565b60006131268560c001518660a001518b613f47565b8a5160208c015160608801516040890151895194955061314794868f6141c3565b935083608001518560000181815161315f91906151c9565b905250608084015160a0860180516131789083906151c9565b905250610100840151604085015160a086015161319591906152f2565b61319f91906152f2565b8560c0018181516131b091906151c9565b9052506131bd86856144be565b95506131d28560c001518660a001518b6145a8565b1560808601525061324d565b836080015180156131fa5750670f43fc2c04ee00008460400151105b156132475761321b89600001518a60200151866060015187600001516145c9565b925082608001518460000181815161323391906151c9565b90525061324085846144be565b945061324d565b50613273565b6001600160a01b031660608401526020830180519061326b8261536b565b905250613027565b505050949350505050565b613286614d6f565b61328e614dbb565b613296614d6f565b600a54858352600060208401526001600160a01b03165b84836020015110156133a857806001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061331b9190615305565b6001600160a01b031660608401819052613335908861216d565b60408401819052670f43fc2c04ee0000111561338b5761335f8989856060015186600001516145c9565b915081608001518360000181815161337791906151c9565b90525061338484836144be565b9350613390565b6133a8565b602083018051906133a08261536b565b9052506132ad565b50505095945050505050565b6000610a71826133ce6103e8670de0b6b3a7640000615266565b6133d990600561527a565b6133e391906152f2565b6133f66064670de0b6b3a7640000615266565b61340190600561527a565b614710565b6001600160a01b03811661345c5760405162461bcd60e51b815260206004820152601e60248201527f4163636f756e742063616e6e6f74206265207a65726f206164647265737300006044820152606401610b18565b803b80610efc5760405162461bcd60e51b815260206004820181905260248201527f4163636f756e7420636f64652073697a652063616e6e6f74206265207a65726f6044820152606401610b18565b6003546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600380546001600160a01b0319169055565b601180546001600160a01b038316600090815260136020526040908190209182556012546001909201829055915491517fc437f324d85e369394148dd9d62f98f534b382e01ed3dd2eb98138fb6d3ab49a92612ee792908252602082015260400190565b600080600061356784610f13565b90506000613574856119f6565b6001600160a01b0386166000908152600d60205260408120600101549192509061359f9084906152f2565b6001600160a01b0387166000908152600d6020526040812054919250906135c79084906152f2565b919791965090945050505050565b6000811561360257816135f168056bc75e2d631000008561527a565b6135fb9190615266565b9050610a71565b50600019610a71565b6000806136166118ba565b905060006136226117ad565b905061362f828286613f47565b949350505050565b61364b6103e8670de0b6b3a7640000615266565b61365690600561527a565b811015801561366d5750670de0b6b3a76400008111155b610a215760405162461bcd60e51b815260206004820152603060248201527f4d6178206665652070657263656e74616765206d75737420626520626574776560448201526f656e20302e352520616e64203130302560801b6064820152608401610b18565b670f43fc2c04ee00006136e48261360b565b1015610a215760405162461bcd60e51b815260206004820152602a60248201527f54726f76654d616e616765723a2043616e6e6f742072656465656d207768656e604482015269102a21a9101e1026a1a960b11b6064820152608401610b18565b60008111610a215760405162461bcd60e51b815260206004820152602e60248201527f54726f76654d616e616765723a20416d6f756e74206d7573742062652067726560448201526d61746572207468616e207a65726f60901b6064820152608401610b18565b6040516370a0823160e01b815281906001600160a01b038516906370a08231906137da908690600401614e03565b602060405180830381865afa1580156137f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061381b91906151dc565b10156124925760405162461bcd60e51b815260206004820152604f60248201527f54726f76654d616e616765723a2052657175657374656420726564656d70746960448201527f6f6e20616d6f756e74206d757374206265203c3d20757365722773205448555360648201526e4420746f6b656e2062616c616e636560881b608482015260a401610b18565b60006001600160a01b03831615806139295750604051630bb7c8fd60e31b81526001600160a01b03851690635dbe47e8906138e6908690600401614e03565b602060405180830381865afa158015613903573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139279190615384565b155b806139445750670f43fc2c04ee0000613942848461216d565b105b1561395157506000612e50565b60405163765e015960e01b81526000906001600160a01b0386169063765e015990613980908790600401614e03565b602060405180830381865afa15801561399d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139c19190615305565b90506001600160a01b0381161580611a405750670f43fc2c04ee00006139e7828561216d565b1095945050505050565b613a17604051806060016040528060008152602001600081526020016000151581525090565b6001600160a01b0387166000908152600d6020526040902054613a4a90879061340190680ad78ebc5ac6200000906151c9565b8082528590613a6290670de0b6b3a76400009061527a565b613a6c9190615266565b60208083019190915281516001600160a01b0389166000908152600d9092526040822054613a9a91906151c9565b6020808401516001600160a01b038b166000908152600d90925260408220600101549293509091613acb91906151c9565b9050680ad78ebc5ac62000008203613b4357613ae68961405a565b613af1896004613da0565b613b068a8a680ad78ebc5ac6200000846147ed565b886001600160a01b031660008051602061540c83398151915260008060006003604051613b369493929190615349565b60405180910390a2613c71565b6000613b4f82846135d5565b90508481141580613b705750686194049f30f7200000613b6e8461494c565b105b15613b845750506001604083015250613c74565b60808b015160405163015f109360e51b81526001600160a01b038c81166004830152602482018490528981166044830152888116606483015290911690632be2126090608401600060405180830381600087803b158015613be457600080fd5b505af1158015613bf8573d6000803e3d6000fd5b5050506001600160a01b038b166000908152600d6020526040902084815560010183905550613c268a612530565b506001600160a01b038a166000818152600d60205260409081902060020154905160008051602061540c83398151915291613c679187918791600390615349565b60405180910390a2505b50505b979650505050505050565b600080613c8a612e57565b9050600083613c99868861527a565b613ca39190615266565b90506000613cb2600283615266565b613cbc90846152f2565b9050613cd081670de0b6b3a7640000614710565b905060008111613ce257613ce26152a7565b600b8190556040518181527fc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600c9060200160405180910390a1610fc4612e9b565b6000610a716121ca610e82565b600082613d44670de0b6b3a76400008661527a565b613d4e9190615266565b905081811115612b355760405162461bcd60e51b815260206004820152601d60248201527f4665652065786365656465642070726f7669646564206d6178696d756d0000006044820152606401610b18565b6000816004811115613db457613db4614f60565b14158015613dd457506001816004811115613dd157613dd1614f60565b14155b613de057613de06152a7565b60145460085460048054604051635f7a196360e11b81526001600160a01b039384169363bef432c693613e17939091169101614e03565b602060405180830381865afa158015613e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e589190615384565b15613e6657613e6681614961565b6001600160a01b0383166000908152600d60205260409020600301805483919060ff19166001836004811115613e9e57613e9e614f60565b02179055506001600160a01b0383166000908152600d60209081526040808320600180820185905590849055601390925282208281550155613ee08382614a3b565b600a54604051631484968760e11b81526001600160a01b03909116906329092d0e90613f10908690600401614e03565b600060405180830381600087803b158015613f2a57600080fd5b505af1158015613f3e573d6000803e3d6000fd5b50505050505050565b60008215613f6f57600083613f5c848761527a565b613f669190615266565b9150612e509050565b50600019612e50565b600080670de0b6b3a7640000613f8e848661527a565b613f989190615266565b9050828110612e505760405162461bcd60e51b815260206004820152603660248201527f54726f76654d616e616765723a2046656520776f756c642065617420757020616044820152751b1b081c995d1d5c9b99590818dbdb1b185d195c985b60521b6064820152608401610b18565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b0381166000908152600d6020526040812060020154600e8054919283926140899084906151c9565b9091555050506001600160a01b03166000908152600d6020526040812060020155565b604051637b7fc9eb60e11b8152600481018390526001600160a01b0384169063f6ff93d690602401600060405180830381600087803b1580156140ee57600080fd5b505af1158015614102573d6000803e3d6000fd5b50506040516342c31a2360e01b8152600481018590526001600160a01b03871692506342c31a239150602401600060405180830381600087803b15801561414857600080fd5b505af115801561415c573d6000803e3d6000fd5b5050604051633ef8ba8160e11b8152600481018490526001600160a01b0386169250637df175029150602401612d25565b6000806010546000036141a1575081610a71565b6000600f54116141b3576141b36152a7565b601054600f54612e46908561527a565b6141cb614d6f565b6141ef60405180606001604052806000815260200160008152602001600081525090565b6014546001106141ff5750613c74565b61420887611a54565b6040850152602084810191909152840181905290835261422790614c0b565b60408301819052680ad78ebc5ac62000006060840152602083015161424c91906151c9565b8152670de0b6b3a76400008611614317576142718989836020015184604001516140ac565b61427a8761405a565b60006080830181905260a0830152815160c0830152805160e08301526142a1876003613da0565b815160208301516040516001600160a01b038a16926000805160206153ec833981519152926142d2926002906153a6565b60405180910390a2866001600160a01b031660008051602061540c8339815191526000806000600260405161430a9493929190615349565b60405180910390a26144b2565b670de0b6b3a7640000861180156143355750670f43fc2c04ee000086105b156143855761434e8989836020015184604001516140ac565b6143578761405a565b81518151614366919087614c18565b60e086015260c085015260a084015260808301526142a1876003613da0565b670f43fc2c04ee0000861015801561439c57508386105b80156143a9575081518510155b156144a1576143c28989836020015184604001516140ac565b846000036143d2576143d26152a7565b6143db8761405a565b6143ee8260000151836020015185614c79565b91506143fb876003613da0565b6101008201511561447057600754610100830151604051633f10abab60e01b81526001600160a01b0390921691633f10abab9161443d918b9190600401615237565b600060405180830381600087803b15801561445757600080fd5b505af115801561446b573d6000803e3d6000fd5b505050505b815160a08301516040516001600160a01b038a16926000805160206153ec833981519152926142d2926002906153a6565b6144a9614d6f565b9150613c749050565b50979650505050505050565b6144c6614d6f565b816040015183604001516144da91906152f2565b6040820152606080830151908401516144f391906152f2565b60608201528151602084015161450991906152f2565b602080830191909152820151835161452191906152f2565b81526080808301519084015161453791906152f2565b608082015260a0808301519084015161455091906152f2565b60a082015260c0808301519084015161456991906152f2565b60c082015260e0808301519084015161458291906152f2565b60e0820152610100808301519084015161459c91906152f2565b61010082015292915050565b6000806145b6858585613f47565b6714d1120d7b1600001195945050505050565b6145d1614d6f565b6145f560405180606001604052806000815260200160008152602001600081525090565b6145fe84611a54565b604085019081526020858101928352860192909252918452905190516146289188918891906140ac565b6146318461405a565b61463e8260200151614c0b565b60408301819052680ad78ebc5ac620000060608401526020830151600091614665916151c9565b905061467683600001518286614c18565b60e087015260c086015260a08501526080840152614695856003613da0565b825160208401516040516001600160a01b038816926000805160206153ec833981519152926146c6926001906153a6565b60405180910390a2846001600160a01b031660008051602061540c833981519152600080600060016040516146fe9493929190615349565b60405180910390a25050949350505050565b600081831061471f5781612e50565b5090919050565b6000603c600c544261473891906151c9565b610e8f9190615266565b6000631f54050082111561475857631f54050091505b8160000361476f5750670de0b6b3a7640000610a71565b670de0b6b3a764000083835b60018111156147e35761478f6002826153c1565b6000036147b4576147a08283614d07565b91506147ad600282615266565b905061477b565b6147be8284614d07565b92506147ca8283614d07565b915060026147d96001836151c9565b6147ad9190615266565b610fc48284614d07565b6040808501516006549151632770a7eb60e21b81526001600160a01b0391821692639dc29fac92614825929116908690600401615237565b600060405180830381600087803b15801561483f57600080fd5b505af1158015614853573d6000803e3d6000fd5b50508551604051637b7fc9eb60e11b8152600481018690526001600160a01b03909116925063f6ff93d69150602401600060405180830381600087803b15801561489c57600080fd5b505af11580156148b0573d6000803e3d6000fd5b505050508360a001516001600160a01b0316633f10abab84836040518363ffffffff1660e01b81526004016148e6929190615237565b600060405180830381600087803b15801561490057600080fd5b505af1158015614914573d6000803e3d6000fd5b5050855160a0870151604051636250216960e01b81526001600160a01b03909216935063625021699250612d25918590600401615237565b6000610a71680ad78ebc5ac6200000836151c9565b6001811180156149dc5750600a546040805163de8fa43160e01b815290516001926001600160a01b03169163de8fa4319160048083019260209291908290030181865afa1580156149b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149da91906151dc565b115b610a215760405162461bcd60e51b815260206004820152602a60248201527f54726f76654d616e616765723a204f6e6c79206f6e652074726f766520696e206044820152697468652073797374656d60b01b6064820152608401610b18565b6001600160a01b0382166000908152600d602052604081206003015460ff1690816004811115614a6d57614a6d614f60565b14158015614a8d57506001816004811115614a8a57614a8a614f60565b14155b614a9957614a996152a7565b6001600160a01b0383166000908152600d602052604081206003015461010090046001600160801b0316908390614ad16001836151c9565b905080836001600160801b03161115614aec57614aec6152a7565b600060148281548110614b0157614b01615291565b600091825260209091200154601480546001600160a01b03909216925082916001600160801b038716908110614b3957614b39615291565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918316808252600d83526040918290206003018054610100600160881b0319166101006001600160801b038a16908102919091179091558251918252928101929092527f02b04ae5f7be9ca7c103293a2aa15f3c339d15d6eda53b721fef7b0e609c831a910160405180910390a16014805480614be057614be06153d5565b600082815260209020810160001990810180546001600160a01b031916905501905550505050505050565b6000610a7160c883615266565b60008080808415614c6357614c2d8786614710565b935086614c3a858861527a565b614c449190615266565b9250614c5084886151c9565b9150614c5c83876151c9565b9050614c70565b5060009250829150859050845b93509350935093565b614c81614d6f565b83815260208101839052600082614ca0670f43fc2c04ee00008761527a565b614caa9190615266565b9050614cb581614c0b565b60408301819052680ad78ebc5ac6200000606084015260808301869052614cdc90826151c9565b60a0830152614ceb81856151c9565b61010083015250600060c0820181905260e08201529392505050565b600080614d14838561527a565b9050670de0b6b3a7640000614d2a600282615266565b614d3490836152f2565b61362f9190615266565b6040518060a00160405280600081526020016000815260200160001515815260200160008152602001600081525090565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060e0016040528060008152602001600081526020016000815260200160006001600160a01b0316815260200160001515815260200160008152602001600081525090565b6001600160a01b0391909116815260200190565b6001600160a01b0381168114610a2157600080fd5b8035614e3781614e17565b919050565b600060208284031215614e4e57600080fd5b8135612e5081614e17565b60008060408385031215614e6c57600080fd5b8235614e7781614e17565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b60006020808385031215614eae57600080fd5b823567ffffffffffffffff80821115614ec657600080fd5b818501915085601f830112614eda57600080fd5b813581811115614eec57614eec614e85565b8060051b604051601f19603f83011681018181108582111715614f1157614f11614e85565b604052918252848201925083810185019188831115614f2f57600080fd5b938501935b82851015614f5457614f4585614e2c565b84529385019392850192614f34565b98975050505050505050565b634e487b7160e01b600052602160045260246000fd5b60058110614f8657614f86614f60565b9052565b60208101610a718284614f76565b600060208284031215614faa57600080fd5b5035919050565b6000806000806000806000806000806101408b8d031215614fd157600080fd5b8a35614fdc81614e17565b995060208b0135614fec81614e17565b985060408b0135614ffc81614e17565b975060608b013561500c81614e17565b965060808b013561501c81614e17565b955060a08b013561502c81614e17565b945060c08b013561503c81614e17565b935060e08b013561504c81614e17565b92506101008b013561505d81614e17565b915061506c6101208c01614e2c565b90509295989b9194979a5092959850565b858152602081018590526040810184905260a0810161509f6060830185614f76565b6001600160801b039290921660809190910152949350505050565b600080604083850312156150cd57600080fd5b82356150d881614e17565b91506020830135600581106150ec57600080fd5b809150509250929050565b600060208083528351808285015260005b8181101561512457858101830151858201604001528201615108565b506000604082860101526040601f19601f8301168501019250505092915050565b600080600080600080600060e0888a03121561516057600080fd5b87359650602088013561517281614e17565b9550604088013561518281614e17565b9450606088013561519281614e17565b9699959850939660808101359560a0820135955060c0909101359350915050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610a7157610a716151b3565b6000602082840312156151ee57600080fd5b5051919050565b60208082526022908201527f54726f76654d616e616765723a206e6f7468696e6720746f206c697175696461604082015261746560f01b606082015260800190565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052601260045260246000fd5b60008261527557615275615250565b500490565b8082028115828204841417610a7157610a716151b3565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b80820180821115610a7157610a716151b3565b60006020828403121561531757600080fd5b8151612e5081614e17565b600081615331576153316151b3565b506000190190565b60048110614f8657614f86614f60565b848152602081018490526040810183905260808101611a406060830184615339565b60006001820161537d5761537d6151b3565b5060010190565b60006020828403121561539657600080fd5b81518015158114612e5057600080fd5b838152602081018390526060810161362f6040830184615339565b6000826153d0576153d0615250565b500690565b634e487b7160e01b600052603160045260246000fdfeea67486ed7ebe3eea8ab3390efd4a3c8aae48be5bea27df104a8af786c408434c3770d654ed33aeea6bf11ac8ef05d02a6a04ed4686dd2f624d853bbec43cc8ba26469706673582212202d04ada939169bc7aa7216586e65c2ce4e3fc480b31b737556f319a5eca7f7f364736f6c63430008110033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106104145760003560e01c8063795d26c311610221578063bcd375261161012b578063d5b35635116100c3578063ee266b8711610087578063ee266b87146109c5578063f2fde38b146109ce578063f36b2425146109e1578063f92d343314610562578063fe2ba848146109e957600080fd5b8063d5b356351461095a578063d66a25531461096d578063d9a7244414610996578063e2ac77b0146109a9578063e6a91f16146109bc57600080fd5b8063bcd37526146108dc578063bf9befb1146108ef578063c52861f2146108f8578063c7b5548114610900578063cbd138ae1461090f578063d293c71014610922578063d380a37c14610935578063d38b05581461093e578063d3d6f8431461094757600080fd5b80639976cf45116101b9578063ae9187541161017d578063ae9187541461085d578063b0d8e18114610870578063b7f8cf9b14610883578063b82f263d14610896578063b91af97c146108a957600080fd5b80639976cf45146107f65780639ba7a40814610809578063a20baee614610720578063a3f4df7e1461081c578063a68b663e1461085457600080fd5b8063795d26c3146107775780637f7dde4a1461077f578063807d138d1461079257806382fe3eb91461079b578063887105d3146107ae5780638da5cb5b146107b65780638f32d59b146107c7578063953f0bb1146107da57806396d711ff146107ed57600080fd5b8063480cd57811610322578063653d46e7116102ba57806372fe25aa1161027e57806372fe25aa14610720578063741bef1a1461072f578063756b253e1461074257806377e16f1e14610755578063794e57241461076857600080fd5b8063653d46e71461068657806366ca4a21146106995780636c37a4af146106a15780636ef64338146106b457806372423c171461070d57600080fd5b8063480cd578146105be5780634870dd9a146105ea57806349eefeee146105f25780634a3c95c4146105fa5780634e443d9e1461060d5780635733d58f146106305780635dba4c4a1461063f578063631203b01461064757806364cee2601461065a57600080fd5b80631f68f20a116103b05780632b11551a116103745780632b11551a1461056a5780632f865568146105725780633cc74225146105855780634597f6ed14610598578063477d66cf146105ab57600080fd5b80631f68f20a146105055780631fd6a4341461050e57806321e378011461051e578063240926691461055a57806328d28b5b1461056257600080fd5b8063048c661d14610419578063071a7541146104425780630b0765571461045857806312610e921461046d57806315d549f1146104805780631673c79a1461049357806318f2817a146104cf5780631bf43555146104e25780631e8b1c2b146104f2575b600080fd5b60055461042c906001600160a01b031681565b6040516104399190614e03565b60405180910390f35b61044a600281565b604051908152602001610439565b61046b610466366004614e3c565b6109fc565b005b61044a61047b366004614e59565b610a24565b61044a61048e366004614e3c565b610a77565b6104ba6104a1366004614e3c565b6013602052600090815260409020805460019091015482565b60408051928352602083019190915201610439565b61044a6104dd366004614e3c565b610a99565b61044a686194049f30f720000081565b61046b610500366004614e9b565b610aac565b61044a600b5481565b61044a680ad78ebc5ac620000081565b61054d61052c366004614e3c565b6001600160a01b03166000908152600d602052604090206003015460ff1690565b6040516104399190614f8a565b61044a610e4d565b61044a610e6e565b61044a610e82565b61046b610580366004614e3c565b610e94565b60015461042c906001600160a01b031681565b60095461042c906001600160a01b031681565b61044a6105b9366004614f98565b610f00565b61044a6105cc366004614e3c565b6001600160a01b03166000908152600d602052604090206001015490565b61044a60c881565b60145461044a565b61044a610608366004614e3c565b610f13565b61062061061b366004614f98565b610fce565b6040519015158152602001610439565b61044a6714d1120d7b16000081565b61046b610fd9565b61044a610655366004614f98565b611045565b61044a610668366004614e3c565b6001600160a01b03166000908152600d602052604090206002015490565b61046b610694366004614f98565b611052565b61044a6113c8565b61046b6106af366004614fb1565b6113da565b6106fc6106c2366004614e3c565b600d6020526000908152604090208054600182015460028301546003909301549192909160ff81169061010090046001600160801b031685565b60405161043995949392919061507d565b61044a61071b366004614e59565b61172b565b61044a670de0b6b3a764000081565b60025461042c906001600160a01b031681565b61042c610750366004614f98565b611783565b60085461042c906001600160a01b031681565b61044a670f43fc2c04ee000081565b61044a6117ad565b60005461042c906001600160a01b031681565b61044a600f5481565b61046b6107a9366004614e3c565b6118a9565b61044a6118ba565b6003546001600160a01b031661042c565b6003546001600160a01b03163314610620565b61046b6107e83660046150ba565b61197f565b61044a60105481565b61044a610804366004614e59565b6119c8565b61044a610817366004614e3c565b6119f6565b6108476040518060400160405280600c81526020016b2a3937bb32a6b0b730b3b2b960a11b81525081565b60405161043991906150f7565b61044a60125481565b600a5461042c906001600160a01b031681565b61044a61087e366004614e3c565b611a22565b60045461042c906001600160a01b031681565b61044a6108a4366004614f98565b611a49565b6108bc6108b7366004614e3c565b611a54565b604080519485526020850193909352918301526060820152608001610439565b61046b6108ea366004615145565b611aac565b61044a600e5481565b61044a612148565b61044a670ddd4b8c6c7d70d881565b61046b61091d366004614e3c565b61215a565b61044a610930366004614e59565b61216d565b61044a600c5481565b61044a60155481565b61044a610955366004614e59565b61218c565b61044a610968366004614f98565b6121bd565b61044a61097b366004614e3c565b6001600160a01b03166000908152600d602052604090205490565b61042c6109a4366004614f98565b6121d0565b6106206109b7366004614e3c565b612200565b61044a60165481565b61044a60115481565b61046b6109dc366004614e3c565b612263565b61044a6122fb565b61046b6109f7366004614e3c565b612308565b610a04612319565b600054600154610a21916001600160a01b0390811691168361239b565b50565b6000610a2e612319565b6001600160a01b0383166000908152600d6020526040812054610a529084906151c9565b6001600160a01b0385166000908152600d602052604090208190559150505b92915050565b6000610a81612319565b610a8a82612497565b6001600160801b031692915050565b6000610aa3612319565b610a7182612530565b8051600003610b215760405162461bcd60e51b815260206004820152603660248201527f54726f76654d616e616765723a2043616c6c646174612061646472657373206160448201527572726179206d757374206e6f7420626520656d70747960501b60648201526084015b60405180910390fd5b6000546001546005546001600160a01b03928316929182169116610b43614d3e565b610b4b614d6f565b600260009054906101000a90046001600160a01b03166001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610ba0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc491906151dc565b826000018181525050826001600160a01b03166301081fda6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2f91906151dc565b60208301528151610c3f906125d5565b1580156040840152610c6757610c608585846000015185602001518a6125f2565b9050610c7f565b610c7c8585846000015185602001518a612827565b90505b6000816020015111610ca35760405162461bcd60e51b8152600401610b18906151f5565b608081015160a082015160405163335525ad60e01b81526001600160a01b0386169263335525ad92610ce092600401918252602082015260400190565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d2685858360c001518460e001516128fc565b61010081015115610d9d57600754610100820151604051636250216960e01b81526001600160a01b0388811693636250216993610d6a939290911691600401615237565b600060405180830381600087803b158015610d8457600080fd5b505af1158015610d98573d6000803e3d6000fd5b505050505b610dab858260400151612b3b565b6020810151606083015261010081015160408201518251610dcc91906151c9565b610dd691906151c9565b6080838101829052606080850151604085810151868401518251938452602084019690965290820152908101929092527f4152c73dd2614c4f9fc35e8c9cf16013cd588c75b49a4c1673ecffdcbcda9403910160405180910390a1610e45853383606001518460400151612c86565b505050505050565b610e606064670de0b6b3a7640000615266565b610e6b90600561527a565b81565b610e606103e8670de0b6b3a7640000615266565b6000610e8f600b54612d5d565b905090565b610e9d81612d9a565b604080516001808252818301909252600091602080830190803683370190505090508181600081518110610ed357610ed3615291565b60200260200101906001600160a01b031690816001600160a01b031681525050610efc81610aac565b5050565b6000610a71610f0d6113c8565b83612e31565b6001600160a01b0381166000908152601360205260408120546011548290610f3c9083906151c9565b9050801580610f7b575060016001600160a01b0385166000908152600d602052604090206003015460ff166004811115610f7857610f78614f60565b14155b15610f8a575060009392505050565b6001600160a01b0384166000908152600d602052604081206002015490670de0b6b3a7640000610fba848461527a565b610fc49190615266565b9695505050505050565b6000610a71826125d5565b610fe1612319565b6000610feb612e57565b9050670de0b6b3a7640000811115611005576110056152a7565b600b8190556040518181527fc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600c9060200160405180910390a1610a21612e9b565b6000610a71610f0d6122fb565b6040805160e081018252600080546001600160a01b0390811683526001548116602084015292820181905260608201819052600a548316608083015260a0820181905260c08201526005549091166110a8614d3e565b6110b0614d6f565b600260009054906101000a90046001600160a01b03166001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611105573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112991906151dc565b826000018181525050826001600160a01b03166301081fda6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611170573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119491906151dc565b602083015281516111a4906125d5565b15801560408401526111cb576111c4848360000151846020015188612ef2565b90506111eb565b6111e884600001518560200151846000015185602001518961327e565b90505b600081602001511161120f5760405162461bcd60e51b8152600401610b18906151f5565b608081015160a082015160405163335525ad60e01b81526001600160a01b0386169263335525ad9261124c92600401918252602082015260400190565b600060405180830381600087803b15801561126657600080fd5b505af115801561127a573d6000803e3d6000fd5b5050505061129a846000015185602001518360c001518460e001516128fc565b61010081015115611311578351600754610100830151604051636250216960e01b81526001600160a01b03938416936362502169936112de93911691600401615237565b600060405180830381600087803b1580156112f857600080fd5b505af115801561130c573d6000803e3d6000fd5b505050505b61132384600001518260400151612b3b565b602081015160608301526101008101516040820151825161134491906151c9565b61134e91906151c9565b6080838101829052606080850151604085810151868401518251938452602084019690965290820152908101929092527f4152c73dd2614c4f9fc35e8c9cf16013cd588c75b49a4c1673ecffdcbcda9403910160405180910390a16113c184600001513383606001518460400151612c86565b5050505050565b6000610e8f6113d5612e57565b6133b4565b6003546001600160a01b031633146114045760405162461bcd60e51b8152600401610b18906152bd565b61140d8a613406565b61141689613406565b61141f88613406565b61142887613406565b61143186613406565b61143a85613406565b61144384613406565b61144c83613406565b61145582613406565b61145e81613406565b600480546001600160a01b03199081166001600160a01b038d8116919091179092556000805482168c84161790556001805482168b84161790556005805482168a8416179055600680548216898416179055600780548216888416179055600280548216878416179055600880548216868416179055600a80548216858416179055600980549091169183169190911790556040517f3ca631ffcd2a9b5d9ae18543fc82f58eb4ca33af9e6ab01b7a8e95331e6ed98590611520908c90614e03565b60405180910390a17f78f058b189175430c48dc02699e3a0031ea4ff781536dc2fab847de4babdd882896040516115579190614e03565b60405180910390a17f5ee0cae2f063ed938bb55046f6a932fb6ae792bf43624806bb90abe68a50be9b8860405161158e9190614e03565b60405180910390a17f82966d27eea39b038ee0fa30cd16532bb24f6e65d31cb58fb227aa5766cdcc7f876040516115c59190614e03565b60405180910390a17fcfb07d791fcafc032b35837b50eb84b74df518cf4cc287e8084f47630fa70fa0866040516115fc9190614e03565b60405180910390a17fe67f36a6e961157d6eff83b91f3af5a62131ceb6f04954ef74f51c1c05e7f88d856040516116339190614e03565b60405180910390a17f8c537274438aa850a330284665d81a85dd38267d09e4050d416bfc94142db2648460405161166a9190614e03565b60405180910390a17fe1e858a66c0bbbcdfa22d58dde1e5d42370be20cdb176e560287f85412e546e0836040516116a19190614e03565b60405180910390a17f65f4cf077bc01e4742eb5ad98326f6e95b63548ea24b17f8d5e823111fe78800826040516116d89190614e03565b60405180910390a17f38335c64466e2376ab931166337e19127650d842036ebe01da1ba3e5c1255ebb8160405161170f9190614e03565b60405180910390a161171f6134ab565b50505050505050505050565b6000611735612319565b6001600160a01b0383166000908152600d602052604081206001015461175c9084906152f2565b6001600160a01b0385166000908152600d6020526040902060010181905591505092915050565b6014818154811061179357600080fd5b6000918252602090912001546001600160a01b0316905081565b600080546040805163f07424e960e01b8152905183926001600160a01b03169163f07424e99160048083019260209291908290030181865afa1580156117f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181b91906151dc565b90506000600160009054906101000a90046001600160a01b03166001600160a01b031663f07424e96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611872573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189691906151dc565b90506118a281836152f2565b9250505090565b6118b1612319565b610a21816134f5565b6000805460408051631529a63960e01b8152905183926001600160a01b031691631529a6399160048083019260209291908290030181865afa158015611904573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061192891906151dc565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631529a6396040518163ffffffff1660e01b8152600401602060405180830381865afa158015611872573d6000803e3d6000fd5b611987612319565b6001600160a01b0382166000908152600d60205260409020600301805482919060ff191660018360048111156119bf576119bf614f60565b02179055505050565b60006119d2612319565b6001600160a01b0383166000908152600d6020526040812054610a529084906152f2565b6001600160a01b0381166000908152601360205260408120600101546012548290610f3c9083906151c9565b6000806000611a3084613559565b915091506000611a4083836135d5565b95945050505050565b6000610a718261360b565b6001600160a01b0381166000908152600d602052604081208054600190910154909180611a80856119f6565b9150611a8b85610f13565b9050611a9782856152f2565b9350611aa381846152f2565b92509193509193565b6040805160e08082018352600080546001600160a01b03908116845260015481166020808601919091526008548216858701526009548216606080870191909152600a548316608080880191909152600754841660a08089019190915260065490941660c080890191909152885161010081018a5286815293840186905297830185905290820184905281018390529081018290529384018190529083015290611b5583613637565b600260009054906101000a90046001600160a01b03166001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bce91906151dc565b60c08201819052611bde906136d2565b611be789613745565b611bf68260400151338b6137ac565b611bfe6117ad565b60e0820152888152608082015160c0820151600091611c1e918b906138a7565b15611c2a575087611d3c565b82608001516001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c909190615305565b90505b6001600160a01b03811615801590611cbf5750670f43fc2c04ee0000611cbd828460c0015161216d565b105b15611d3c5782608001516001600160a01b031663b72703ac826040518263ffffffff1660e01b8152600401611cf49190614e03565b602060405180830381865afa158015611d11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d359190615305565b9050611c93565b84600003611d4a5760001994505b6001600160a01b03811615801590611d625750815115155b8015611d6e5750600085115b15611e815784611d7d81615322565b955050600083608001516001600160a01b031663b72703ac836040518263ffffffff1660e01b8152600401611db29190614e03565b602060405180830381865afa158015611dcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df39190615305565b9050611e08846000015185602001518461239b565b6000611e21858486600001518760c001518e8e8e6139f1565b9050806040015115611e34575050611e81565b8051602085018051611e479083906152f2565b9052506020810151604085018051611e609083906152f2565b905250805184518590611e749083906151c9565b905250909150611d4a9050565b6000826040015111611ee75760405162461bcd60e51b815260206004820152602960248201527f54726f76654d616e616765723a20556e61626c6520746f2072656465656d20616044820152681b9e48185b5bdd5b9d60ba1b6064820152608401610b18565b611efe82604001518360c001518460e00151613c7f565b50611f0c8260400151613d22565b606083018190526040830151611f23919086613d2f565b825160608085015190840151604051636250216960e01b81526001600160a01b0390931692636250216992611f5c929091600401615237565b600060405180830381600087803b158015611f7657600080fd5b505af1158015611f8a573d6000803e3d6000fd5b50505060608301516040840151611fa192506151c9565b60808301526020820151604080840151606085015191517f43a3f4082a4dbc33d78e317d2497d3a730bc7fc3574159dcea1056e62e5d9ad893611ffd938f93919293845260208401929092526040830152606082015260800190565b60405180910390a182604001516001600160a01b0316639dc29fac3384602001516040518363ffffffff1660e01b815260040161203b929190615237565b600060405180830381600087803b15801561205557600080fd5b505af1158015612069573d6000803e3d6000fd5b505084516020850151604051637b7fc9eb60e11b81526001600160a01b03909216935063f6ff93d692506120a39160040190815260200190565b600060405180830381600087803b1580156120bd57600080fd5b505af11580156120d1573d6000803e3d6000fd5b505084516080850151604051636250216960e01b81526001600160a01b0390921693506362502169925061210a91339190600401615237565b600060405180830381600087803b15801561212457600080fd5b505af1158015612138573d6000803e3d6000fd5b5050505050505050505050505050565b6000610e8f612155612e57565b612d5d565b612162612319565b610a21816002613da0565b600080600061217b85613559565b915091506000610fc4838387613f47565b6000612196612319565b6001600160a01b0383166000908152600d602052604081206001015461175c9084906151c9565b6000610a716121ca612148565b83613f78565b6000601482815481106121e5576121e5615291565b6000918252602090912001546001600160a01b031692915050565b600060016001600160a01b0383166000908152600d602052604090206003015460ff16600481111561223457612234614f60565b1461224157506000919050565b506011546001600160a01b039091166000908152601360205260409020541090565b6003546001600160a01b0316331461228d5760405162461bcd60e51b8152600401610b18906152bd565b6001600160a01b0381166122f25760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610b18565b610a2181614008565b6000610e8f600b546133b4565b612310612319565b610a218161405a565b6004546001600160a01b031633146123995760405162461bcd60e51b815260206004820152603b60248201527f54726f76654d616e616765723a2043616c6c6572206973206e6f74207468652060448201527f426f72726f7765724f7065726174696f6e7320636f6e747261637400000000006064820152608401610b18565b565b6123a481612200565b15612492576123b281612d9a565b60006123bd82610f13565b905060006123ca836119f6565b6001600160a01b0384166000908152600d60205260408120600101805492935084929091906123fa9084906152f2565b90915550506001600160a01b0383166000908152600d6020526040812080548392906124279084906152f2565b909155506124369050836134f5565b612442858583856140ac565b6001600160a01b0383166000818152600d602052604080822080546001820154600290920154925160008051602061540c833981519152946124879492939291615349565b60405180910390a250505b505050565b601480546001808201835560008381527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec90920180546001600160a01b0319166001600160a01b038616179055915490916124f1916151c9565b6001600160a01b03929092166000908152600d602052604090206003018054610100600160881b0319166101006001600160801b038516021790555090565b6001600160a01b0381166000908152600d602052604081206001015481906125579061418d565b6001600160a01b0384166000908152600d60205260409020600201805490829055600e5491925090829061258c9083906151c9565b61259691906152f2565b600e8190556040519081527f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae5203829060200160405180910390a15092915050565b6000806125e18361360b565b6714d1120d7b160000119392505050565b6125fa614d6f565b612602614dbb565b61260a614d6f565b8482526000608083015261261c6117ad565b60a08301526126296118ba565b60c0830152600060208301525b83518260200151101561281c578382602001518151811061265957612659615291565b6020908102919091018101516001600160a01b0316606084018190526000908152600d909152604090206003015460019060ff16600481111561269e5761269e614f60565b03612804576126b182606001518761216d565b604083015260808201516127a757670f43fc2c04ee00008260400151101580156126da57508151155b6128045760006126f38360c001518460a0015189613f47565b90506127108989856060015186604001518760000151868d6141c3565b915081608001518360000181815161272891906151c9565b905250608082015160a0840180516127419083906151c9565b905250610100820151604083015160a084015161275e91906152f2565b61276891906152f2565b8360c00181815161277991906151c9565b90525061278684836144be565b935061279b8360c001518460a00151896145a8565b15608084015250612804565b816080015180156127c35750670f43fc2c04ee00008260400151105b15612804576127dc8888846060015185600001516145c9565b90508060800151826000018181516127f491906151c9565b90525061280183826144be565b92505b602082018051906128148261536b565b905250612636565b505095945050505050565b61282f614d6f565b612837614dbb565b61283f614d6f565b848252600060208301525b83518260200151101561281c578382602001518151811061286d5761286d615291565b60209081029190910101516001600160a01b031660608301819052612892908761216d565b60408301819052670f43fc2c04ee000011156128e4576128bc8888846060015185600001516145c9565b90508060800151826000018181516128d491906151c9565b9052506128e183826144be565b92505b602082018051906128f48261536b565b90525061284a565b8115612b355760155460009061291a670de0b6b3a76400008461527a565b61292491906152f2565b90506000601654670de0b6b3a76400008561293f919061527a565b61294991906152f2565b90506000600e548361295b9190615266565b90506000600e548361296d9190615266565b9050600e548261297d919061527a565b61298790856151c9565b601555600e54612997908261527a565b6129a190846151c9565b60168190555081601160008282546129b991906152f2565b9250508190555080601260008282546129d291906152f2565b90915550506011546012546040517f9f8bc8ab0daf5bceef75ecfd2085d1fcc6548c657ea970d9a23a60610d0737e392612a1492908252602082015260400190565b60405180910390a1604051637b7fc9eb60e11b8152600481018790526001600160a01b0389169063f6ff93d690602401600060405180830381600087803b158015612a5e57600080fd5b505af1158015612a72573d6000803e3d6000fd5b50506040516342c31a2360e01b8152600481018990526001600160a01b038a1692506342c31a239150602401600060405180830381600087803b158015612ab857600080fd5b505af1158015612acc573d6000803e3d6000fd5b5050604051636250216960e01b81526001600160a01b038b16925063625021699150612afe908a908990600401615237565b600060405180830381600087803b158015612b1857600080fd5b505af1158015612b2c573d6000803e3d6000fd5b50505050505050505b50505050565b600e54600f819055506000826001600160a01b0316631529a6396040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba891906151dc565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631529a6396040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2391906151dc565b905080612c3084846151c9565b612c3a91906152f2565b6010819055600f546040517f51bf4c63ec3cba9d03d43238abbdd979dd91bd16d9895c74ceea9118c7baaf6092612c78928252602082015260400190565b60405180910390a150505050565b8115612cf1576006546040516316268b0d60e21b81526001600160a01b039091169063589a2c3490612cbe9086908690600401615237565b600060405180830381600087803b158015612cd857600080fd5b505af1158015612cec573d6000803e3d6000fd5b505050505b8015612b3557604051636250216960e01b81526001600160a01b03851690636250216990612d259086908590600401615237565b600060405180830381600087803b158015612d3f57600080fd5b505af1158015612d53573d6000803e3d6000fd5b5050505050505050565b6000610a7182612d776103e8670de0b6b3a7640000615266565b612d8290600561527a565b612d8c91906152f2565b670de0b6b3a7640000614710565b60016001600160a01b0382166000908152600d602052604090206003015460ff166004811115612dcc57612dcc614f60565b14610a215760405162461bcd60e51b815260206004820152602f60248201527f54726f76654d616e616765723a2054726f766520646f6573206e6f742065786960448201526e1cdd081bdc881a5cc818db1bdcd959608a1b6064820152608401610b18565b6000670de0b6b3a7640000612e46838561527a565b612e509190615266565b9392505050565b600080612e62614726565b90506000612e78670ddd4b8c6c7d70d883614742565b9050670de0b6b3a764000081600b54612e91919061527a565b6118a29190615266565b6000600c5442612eab91906151c9565b9050603c8110610a215742600c8190556040519081527f860f8d2f0c74dd487e89e2883e3b25b8159ce1e1b3433a291cba7b82c508f3bc906020015b60405180910390a150565b612efa614d6f565b612f02614dbb565b612f0a614d6f565b84825260006080830152612f1c6117ad565b60a0830152612f296118ba565b8260c001818152505086608001516001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f989190615305565b82606001906001600160a01b031690816001600160a01b031681525050600087608001516001600160a01b0316631e2231436040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ff9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061301d9190615305565b6000602085015290505b8483602001511080156130505750806001600160a01b031683606001516001600160a01b031614155b156132735760808801516060840151604051632dc9c0eb60e21b81526000926001600160a01b03169163b72703ac9161308c9190600401614e03565b602060405180830381865afa1580156130a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130cd9190615305565b90506130dd84606001518961216d565b604085015260808401516131de57670f43fc2c04ee000084604001511015801561310657508351155b156131115750613273565b60006131268560c001518660a001518b613f47565b8a5160208c015160608801516040890151895194955061314794868f6141c3565b935083608001518560000181815161315f91906151c9565b905250608084015160a0860180516131789083906151c9565b905250610100840151604085015160a086015161319591906152f2565b61319f91906152f2565b8560c0018181516131b091906151c9565b9052506131bd86856144be565b95506131d28560c001518660a001518b6145a8565b1560808601525061324d565b836080015180156131fa5750670f43fc2c04ee00008460400151105b156132475761321b89600001518a60200151866060015187600001516145c9565b925082608001518460000181815161323391906151c9565b90525061324085846144be565b945061324d565b50613273565b6001600160a01b031660608401526020830180519061326b8261536b565b905250613027565b505050949350505050565b613286614d6f565b61328e614dbb565b613296614d6f565b600a54858352600060208401526001600160a01b03165b84836020015110156133a857806001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061331b9190615305565b6001600160a01b031660608401819052613335908861216d565b60408401819052670f43fc2c04ee0000111561338b5761335f8989856060015186600001516145c9565b915081608001518360000181815161337791906151c9565b90525061338484836144be565b9350613390565b6133a8565b602083018051906133a08261536b565b9052506132ad565b50505095945050505050565b6000610a71826133ce6103e8670de0b6b3a7640000615266565b6133d990600561527a565b6133e391906152f2565b6133f66064670de0b6b3a7640000615266565b61340190600561527a565b614710565b6001600160a01b03811661345c5760405162461bcd60e51b815260206004820152601e60248201527f4163636f756e742063616e6e6f74206265207a65726f206164647265737300006044820152606401610b18565b803b80610efc5760405162461bcd60e51b815260206004820181905260248201527f4163636f756e7420636f64652073697a652063616e6e6f74206265207a65726f6044820152606401610b18565b6003546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600380546001600160a01b0319169055565b601180546001600160a01b038316600090815260136020526040908190209182556012546001909201829055915491517fc437f324d85e369394148dd9d62f98f534b382e01ed3dd2eb98138fb6d3ab49a92612ee792908252602082015260400190565b600080600061356784610f13565b90506000613574856119f6565b6001600160a01b0386166000908152600d60205260408120600101549192509061359f9084906152f2565b6001600160a01b0387166000908152600d6020526040812054919250906135c79084906152f2565b919791965090945050505050565b6000811561360257816135f168056bc75e2d631000008561527a565b6135fb9190615266565b9050610a71565b50600019610a71565b6000806136166118ba565b905060006136226117ad565b905061362f828286613f47565b949350505050565b61364b6103e8670de0b6b3a7640000615266565b61365690600561527a565b811015801561366d5750670de0b6b3a76400008111155b610a215760405162461bcd60e51b815260206004820152603060248201527f4d6178206665652070657263656e74616765206d75737420626520626574776560448201526f656e20302e352520616e64203130302560801b6064820152608401610b18565b670f43fc2c04ee00006136e48261360b565b1015610a215760405162461bcd60e51b815260206004820152602a60248201527f54726f76654d616e616765723a2043616e6e6f742072656465656d207768656e604482015269102a21a9101e1026a1a960b11b6064820152608401610b18565b60008111610a215760405162461bcd60e51b815260206004820152602e60248201527f54726f76654d616e616765723a20416d6f756e74206d7573742062652067726560448201526d61746572207468616e207a65726f60901b6064820152608401610b18565b6040516370a0823160e01b815281906001600160a01b038516906370a08231906137da908690600401614e03565b602060405180830381865afa1580156137f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061381b91906151dc565b10156124925760405162461bcd60e51b815260206004820152604f60248201527f54726f76654d616e616765723a2052657175657374656420726564656d70746960448201527f6f6e20616d6f756e74206d757374206265203c3d20757365722773205448555360648201526e4420746f6b656e2062616c616e636560881b608482015260a401610b18565b60006001600160a01b03831615806139295750604051630bb7c8fd60e31b81526001600160a01b03851690635dbe47e8906138e6908690600401614e03565b602060405180830381865afa158015613903573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139279190615384565b155b806139445750670f43fc2c04ee0000613942848461216d565b105b1561395157506000612e50565b60405163765e015960e01b81526000906001600160a01b0386169063765e015990613980908790600401614e03565b602060405180830381865afa15801561399d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139c19190615305565b90506001600160a01b0381161580611a405750670f43fc2c04ee00006139e7828561216d565b1095945050505050565b613a17604051806060016040528060008152602001600081526020016000151581525090565b6001600160a01b0387166000908152600d6020526040902054613a4a90879061340190680ad78ebc5ac6200000906151c9565b8082528590613a6290670de0b6b3a76400009061527a565b613a6c9190615266565b60208083019190915281516001600160a01b0389166000908152600d9092526040822054613a9a91906151c9565b6020808401516001600160a01b038b166000908152600d90925260408220600101549293509091613acb91906151c9565b9050680ad78ebc5ac62000008203613b4357613ae68961405a565b613af1896004613da0565b613b068a8a680ad78ebc5ac6200000846147ed565b886001600160a01b031660008051602061540c83398151915260008060006003604051613b369493929190615349565b60405180910390a2613c71565b6000613b4f82846135d5565b90508481141580613b705750686194049f30f7200000613b6e8461494c565b105b15613b845750506001604083015250613c74565b60808b015160405163015f109360e51b81526001600160a01b038c81166004830152602482018490528981166044830152888116606483015290911690632be2126090608401600060405180830381600087803b158015613be457600080fd5b505af1158015613bf8573d6000803e3d6000fd5b5050506001600160a01b038b166000908152600d6020526040902084815560010183905550613c268a612530565b506001600160a01b038a166000818152600d60205260409081902060020154905160008051602061540c83398151915291613c679187918791600390615349565b60405180910390a2505b50505b979650505050505050565b600080613c8a612e57565b9050600083613c99868861527a565b613ca39190615266565b90506000613cb2600283615266565b613cbc90846152f2565b9050613cd081670de0b6b3a7640000614710565b905060008111613ce257613ce26152a7565b600b8190556040518181527fc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600c9060200160405180910390a1610fc4612e9b565b6000610a716121ca610e82565b600082613d44670de0b6b3a76400008661527a565b613d4e9190615266565b905081811115612b355760405162461bcd60e51b815260206004820152601d60248201527f4665652065786365656465642070726f7669646564206d6178696d756d0000006044820152606401610b18565b6000816004811115613db457613db4614f60565b14158015613dd457506001816004811115613dd157613dd1614f60565b14155b613de057613de06152a7565b60145460085460048054604051635f7a196360e11b81526001600160a01b039384169363bef432c693613e17939091169101614e03565b602060405180830381865afa158015613e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e589190615384565b15613e6657613e6681614961565b6001600160a01b0383166000908152600d60205260409020600301805483919060ff19166001836004811115613e9e57613e9e614f60565b02179055506001600160a01b0383166000908152600d60209081526040808320600180820185905590849055601390925282208281550155613ee08382614a3b565b600a54604051631484968760e11b81526001600160a01b03909116906329092d0e90613f10908690600401614e03565b600060405180830381600087803b158015613f2a57600080fd5b505af1158015613f3e573d6000803e3d6000fd5b50505050505050565b60008215613f6f57600083613f5c848761527a565b613f669190615266565b9150612e509050565b50600019612e50565b600080670de0b6b3a7640000613f8e848661527a565b613f989190615266565b9050828110612e505760405162461bcd60e51b815260206004820152603660248201527f54726f76654d616e616765723a2046656520776f756c642065617420757020616044820152751b1b081c995d1d5c9b99590818dbdb1b185d195c985b60521b6064820152608401610b18565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b0381166000908152600d6020526040812060020154600e8054919283926140899084906151c9565b9091555050506001600160a01b03166000908152600d6020526040812060020155565b604051637b7fc9eb60e11b8152600481018390526001600160a01b0384169063f6ff93d690602401600060405180830381600087803b1580156140ee57600080fd5b505af1158015614102573d6000803e3d6000fd5b50506040516342c31a2360e01b8152600481018590526001600160a01b03871692506342c31a239150602401600060405180830381600087803b15801561414857600080fd5b505af115801561415c573d6000803e3d6000fd5b5050604051633ef8ba8160e11b8152600481018490526001600160a01b0386169250637df175029150602401612d25565b6000806010546000036141a1575081610a71565b6000600f54116141b3576141b36152a7565b601054600f54612e46908561527a565b6141cb614d6f565b6141ef60405180606001604052806000815260200160008152602001600081525090565b6014546001106141ff5750613c74565b61420887611a54565b6040850152602084810191909152840181905290835261422790614c0b565b60408301819052680ad78ebc5ac62000006060840152602083015161424c91906151c9565b8152670de0b6b3a76400008611614317576142718989836020015184604001516140ac565b61427a8761405a565b60006080830181905260a0830152815160c0830152805160e08301526142a1876003613da0565b815160208301516040516001600160a01b038a16926000805160206153ec833981519152926142d2926002906153a6565b60405180910390a2866001600160a01b031660008051602061540c8339815191526000806000600260405161430a9493929190615349565b60405180910390a26144b2565b670de0b6b3a7640000861180156143355750670f43fc2c04ee000086105b156143855761434e8989836020015184604001516140ac565b6143578761405a565b81518151614366919087614c18565b60e086015260c085015260a084015260808301526142a1876003613da0565b670f43fc2c04ee0000861015801561439c57508386105b80156143a9575081518510155b156144a1576143c28989836020015184604001516140ac565b846000036143d2576143d26152a7565b6143db8761405a565b6143ee8260000151836020015185614c79565b91506143fb876003613da0565b6101008201511561447057600754610100830151604051633f10abab60e01b81526001600160a01b0390921691633f10abab9161443d918b9190600401615237565b600060405180830381600087803b15801561445757600080fd5b505af115801561446b573d6000803e3d6000fd5b505050505b815160a08301516040516001600160a01b038a16926000805160206153ec833981519152926142d2926002906153a6565b6144a9614d6f565b9150613c749050565b50979650505050505050565b6144c6614d6f565b816040015183604001516144da91906152f2565b6040820152606080830151908401516144f391906152f2565b60608201528151602084015161450991906152f2565b602080830191909152820151835161452191906152f2565b81526080808301519084015161453791906152f2565b608082015260a0808301519084015161455091906152f2565b60a082015260c0808301519084015161456991906152f2565b60c082015260e0808301519084015161458291906152f2565b60e0820152610100808301519084015161459c91906152f2565b61010082015292915050565b6000806145b6858585613f47565b6714d1120d7b1600001195945050505050565b6145d1614d6f565b6145f560405180606001604052806000815260200160008152602001600081525090565b6145fe84611a54565b604085019081526020858101928352860192909252918452905190516146289188918891906140ac565b6146318461405a565b61463e8260200151614c0b565b60408301819052680ad78ebc5ac620000060608401526020830151600091614665916151c9565b905061467683600001518286614c18565b60e087015260c086015260a08501526080840152614695856003613da0565b825160208401516040516001600160a01b038816926000805160206153ec833981519152926146c6926001906153a6565b60405180910390a2846001600160a01b031660008051602061540c833981519152600080600060016040516146fe9493929190615349565b60405180910390a25050949350505050565b600081831061471f5781612e50565b5090919050565b6000603c600c544261473891906151c9565b610e8f9190615266565b6000631f54050082111561475857631f54050091505b8160000361476f5750670de0b6b3a7640000610a71565b670de0b6b3a764000083835b60018111156147e35761478f6002826153c1565b6000036147b4576147a08283614d07565b91506147ad600282615266565b905061477b565b6147be8284614d07565b92506147ca8283614d07565b915060026147d96001836151c9565b6147ad9190615266565b610fc48284614d07565b6040808501516006549151632770a7eb60e21b81526001600160a01b0391821692639dc29fac92614825929116908690600401615237565b600060405180830381600087803b15801561483f57600080fd5b505af1158015614853573d6000803e3d6000fd5b50508551604051637b7fc9eb60e11b8152600481018690526001600160a01b03909116925063f6ff93d69150602401600060405180830381600087803b15801561489c57600080fd5b505af11580156148b0573d6000803e3d6000fd5b505050508360a001516001600160a01b0316633f10abab84836040518363ffffffff1660e01b81526004016148e6929190615237565b600060405180830381600087803b15801561490057600080fd5b505af1158015614914573d6000803e3d6000fd5b5050855160a0870151604051636250216960e01b81526001600160a01b03909216935063625021699250612d25918590600401615237565b6000610a71680ad78ebc5ac6200000836151c9565b6001811180156149dc5750600a546040805163de8fa43160e01b815290516001926001600160a01b03169163de8fa4319160048083019260209291908290030181865afa1580156149b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149da91906151dc565b115b610a215760405162461bcd60e51b815260206004820152602a60248201527f54726f76654d616e616765723a204f6e6c79206f6e652074726f766520696e206044820152697468652073797374656d60b01b6064820152608401610b18565b6001600160a01b0382166000908152600d602052604081206003015460ff1690816004811115614a6d57614a6d614f60565b14158015614a8d57506001816004811115614a8a57614a8a614f60565b14155b614a9957614a996152a7565b6001600160a01b0383166000908152600d602052604081206003015461010090046001600160801b0316908390614ad16001836151c9565b905080836001600160801b03161115614aec57614aec6152a7565b600060148281548110614b0157614b01615291565b600091825260209091200154601480546001600160a01b03909216925082916001600160801b038716908110614b3957614b39615291565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918316808252600d83526040918290206003018054610100600160881b0319166101006001600160801b038a16908102919091179091558251918252928101929092527f02b04ae5f7be9ca7c103293a2aa15f3c339d15d6eda53b721fef7b0e609c831a910160405180910390a16014805480614be057614be06153d5565b600082815260209020810160001990810180546001600160a01b031916905501905550505050505050565b6000610a7160c883615266565b60008080808415614c6357614c2d8786614710565b935086614c3a858861527a565b614c449190615266565b9250614c5084886151c9565b9150614c5c83876151c9565b9050614c70565b5060009250829150859050845b93509350935093565b614c81614d6f565b83815260208101839052600082614ca0670f43fc2c04ee00008761527a565b614caa9190615266565b9050614cb581614c0b565b60408301819052680ad78ebc5ac6200000606084015260808301869052614cdc90826151c9565b60a0830152614ceb81856151c9565b61010083015250600060c0820181905260e08201529392505050565b600080614d14838561527a565b9050670de0b6b3a7640000614d2a600282615266565b614d3490836152f2565b61362f9190615266565b6040518060a00160405280600081526020016000815260200160001515815260200160008152602001600081525090565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060e0016040528060008152602001600081526020016000815260200160006001600160a01b0316815260200160001515815260200160008152602001600081525090565b6001600160a01b0391909116815260200190565b6001600160a01b0381168114610a2157600080fd5b8035614e3781614e17565b919050565b600060208284031215614e4e57600080fd5b8135612e5081614e17565b60008060408385031215614e6c57600080fd5b8235614e7781614e17565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b60006020808385031215614eae57600080fd5b823567ffffffffffffffff80821115614ec657600080fd5b818501915085601f830112614eda57600080fd5b813581811115614eec57614eec614e85565b8060051b604051601f19603f83011681018181108582111715614f1157614f11614e85565b604052918252848201925083810185019188831115614f2f57600080fd5b938501935b82851015614f5457614f4585614e2c565b84529385019392850192614f34565b98975050505050505050565b634e487b7160e01b600052602160045260246000fd5b60058110614f8657614f86614f60565b9052565b60208101610a718284614f76565b600060208284031215614faa57600080fd5b5035919050565b6000806000806000806000806000806101408b8d031215614fd157600080fd5b8a35614fdc81614e17565b995060208b0135614fec81614e17565b985060408b0135614ffc81614e17565b975060608b013561500c81614e17565b965060808b013561501c81614e17565b955060a08b013561502c81614e17565b945060c08b013561503c81614e17565b935060e08b013561504c81614e17565b92506101008b013561505d81614e17565b915061506c6101208c01614e2c565b90509295989b9194979a5092959850565b858152602081018590526040810184905260a0810161509f6060830185614f76565b6001600160801b039290921660809190910152949350505050565b600080604083850312156150cd57600080fd5b82356150d881614e17565b91506020830135600581106150ec57600080fd5b809150509250929050565b600060208083528351808285015260005b8181101561512457858101830151858201604001528201615108565b506000604082860101526040601f19601f8301168501019250505092915050565b600080600080600080600060e0888a03121561516057600080fd5b87359650602088013561517281614e17565b9550604088013561518281614e17565b9450606088013561519281614e17565b9699959850939660808101359560a0820135955060c0909101359350915050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610a7157610a716151b3565b6000602082840312156151ee57600080fd5b5051919050565b60208082526022908201527f54726f76654d616e616765723a206e6f7468696e6720746f206c697175696461604082015261746560f01b606082015260800190565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052601260045260246000fd5b60008261527557615275615250565b500490565b8082028115828204841417610a7157610a716151b3565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b80820180821115610a7157610a716151b3565b60006020828403121561531757600080fd5b8151612e5081614e17565b600081615331576153316151b3565b506000190190565b60048110614f8657614f86614f60565b848152602081018490526040810183905260808101611a406060830184615339565b60006001820161537d5761537d6151b3565b5060010190565b60006020828403121561539657600080fd5b81518015158114612e5057600080fd5b838152602081018390526060810161362f6040830184615339565b6000826153d0576153d0615250565b500690565b634e487b7160e01b600052603160045260246000fdfeea67486ed7ebe3eea8ab3390efd4a3c8aae48be5bea27df104a8af786c408434c3770d654ed33aeea6bf11ac8ef05d02a6a04ed4686dd2f624d853bbec43cc8ba26469706673582212202d04ada939169bc7aa7216586e65c2ce4e3fc480b31b737556f319a5eca7f7f364736f6c63430008110033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.