ETH Price: $3,138.27 (-1.61%)

Contract

0x1CAdAfa5A6ce2cCE187057541a50B6389394F761

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PositionManager

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 1 runs

Other Settings:
cancun EvmVersion
File 1 of 19 : PositionManager.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import { IERC3156FlashBorrower } from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
import "../interfaces/core/IBorrowerOperations.sol";
import "../interfaces/core/IDebtToken.sol";
import "../interfaces/core/ISortedPositions.sol";
import "../interfaces/core/IPriceFeed.sol";
import "../interfaces/core/IFactory.sol";
import "../interfaces/core/ILiquidationManager.sol";
import "../dependencies/SystemStart.sol";
import "../dependencies/PropBase.sol";
import "../dependencies/PropMath.sol";
import "../dependencies/PropOwnable.sol";

/**
    @title Position Manager
    @notice Based on Liquity's `TroveManager`
            https://github.com/liquity/dev/blob/main/packages/contracts/contracts/TroveManager.sol

            Implementation is modified so that multiple `PositionManager` and `SortedPositions`
            contracts are deployed in tandem, with each pair managing positions of a single collateral
            type.

            Functionality related to liquidations has been moved to `LiquidationManager`. This was
            necessary to avoid the restriction on deployed bytecode size.
 */
contract PositionManager is PropBase, PropOwnable, SystemStart {
    using SafeERC20 for IERC20;

    address public immutable borrowerOperations;
    address public immutable liquidationManager;
    address immutable gasPoolAddress;
    IDebtToken public immutable debtToken;
    
    address public collVaultRouter;
    IPriceFeed public priceFeed;
    IERC20 public collateralToken;

    // A doubly linked list of Positions, sorted by their collateral ratios
    ISortedPositions public sortedPositions;

    // Minimum collateral ratio for individual positions
    uint256 public MCR;

    uint256 constant SECONDS_IN_ONE_MINUTE = 60;
    uint256 constant INTEREST_PRECISION = 1e27;
    uint256 constant SECONDS_IN_YEAR = 365 days;

    uint256 public constant SUNSETTING_INTEREST_RATE = (INTEREST_PRECISION * 5000) / (BP * SECONDS_IN_YEAR); // 50%

    uint256 constant _100pct = 1000000000000000000; // 1e18 == 100%, below this CR it's considered undercollateralized

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

    uint16 constant BP = 1e4;

    // --- ERC 3156 Data ---
    bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");

    // commented values are Liquity's fixed settings for each parameter
    uint256 public minuteDecayFactor; // 999037758833783000  (half-life of 12 hours)
    /// @dev Redemption fee floor should be higher than deviation threshold of oracle supporting this collateral
    uint256 public redemptionFeeFloor; // DECIMAL_PRECISION / 1000 * 5  (0.5%)
    uint256 public maxRedemptionFee; // DECIMAL_PRECISION  (100%)
    uint256 public borrowingFeeFloor; // DECIMAL_PRECISION / 1000 * 5  (0.5%)
    uint256 public maxBorrowingFee; // DECIMAL_PRECISION / 100 * 5  (5%)
    uint256 public maxSystemDebt;

    uint256 public interestRate;
    uint256 public activeInterestIndex;
    uint256 public lastActiveIndexUpdate;

    uint256 public systemDeploymentTime;
    bool public sunsetting;
    bool public paused;

    uint256 public baseRate;

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

    uint256 public totalStakes;

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

    // Snapshot of the total collateral taken immediately after the latest liquidation.
    uint256 public totalCollateralSnapshot;

    /*
     * L_collateral and L_debt 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 debt increase  of ( stake * [L_debt - L_debt(0)] )
     *
     * Where L_collateral(0) and L_debt(0) are snapshots of L_collateral and L_debt for the active Position taken at the instant the stake was made
     */
    uint256 public L_collateral;
    uint256 public L_debt;

    // Error trackers for the position redistribution calculation
    uint256 public lastCollateralError_Redistribution;
    uint256 public lastDebtError_Redistribution;

    uint256 internal totalActiveCollateral;
    uint256 internal totalActiveDebt;
    uint256 public interestPayable;
    address public interestReceiver;

    uint256 public defaultedCollateral;
    uint256 public defaultedDebt;

    mapping(address => Position) public Positions;
    mapping(address => uint256) public surplusBalances;

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

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

    // Store the necessary data for a position
    struct Position {
        uint256 debt;
        uint256 coll;
        uint256 stake;
        Status status;
        uint128 arrayIndex;
        uint256 activeInterestIndex;
    }

    struct RedemptionTotals {
        uint256 remainingDebt;
        uint256 totalDebtToRedeem;
        uint256 totalCollateralDrawn;
        uint256 collateralFee;
        uint256 collateralToSendToRedeemer;
        uint256 decayedBaseRate;
        uint256 price;
        uint256 totalDebtSupplyAtStart;
    }

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

    // Object containing the collateral and debt snapshots for a given active position
    struct RewardSnapshot {
        uint256 collateral;
        uint256 debt;
    }

    enum PositionManagerOperation {
        applyPendingRewards,
        liquidateInNormalMode,
        liquidateInRecoveryMode,
        redeemCollateral
    }

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

    event NewParameters(IFactory.DeploymentParams params);
    event PriceFeedUpdated(address _priceFeed);
    event PositionUpdated(
        address indexed _borrower,
        uint256 _debt,
        uint256 _coll,
        uint256 _stake,
        PositionManagerOperation _operation
    );
    event Redemption(
        address indexed _redeemer,
        uint256 _attemptedDebtAmount,
        uint256 _actualDebtAmount,
        uint256 _collateralSent,
        uint256 _collateralFee
    );
    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_debt);
    event PositionSnapshotsUpdated(uint256 _L_collateral, uint256 _L_debt);
    event PositionIndexUpdated(address _borrower, uint256 _newIndex);
    event CollateralSent(address _to, uint256 _amount);
    event InterestReceiverSet(address indexed _interestReceiver);

    modifier whenNotPaused() {
        require(!paused, "Collateral Paused");
        _;
    }

    /// @dev Overrided by PermissionedPositionManager to enforce only one position
    function _isPermissionedCheck(address _borrower) internal virtual {}

    constructor(
        address _core,
        address _gasPoolAddress,
        address _debtTokenAddress,
        address _borrowerOperations,
        address _liquidationManager,
        uint256 _gasCompensation
    ) PropOwnable(_core) PropBase(_gasCompensation) SystemStart(_core) {
        if (_core == address(0) || _gasPoolAddress == address(0) || _debtTokenAddress == address(0) || _borrowerOperations == address(0) || _liquidationManager == address(0)) {
            revert("PositionManager: 0 address");
        }

        gasPoolAddress = _gasPoolAddress;
        debtToken = IDebtToken(_debtTokenAddress);
        borrowerOperations = _borrowerOperations;
        liquidationManager = _liquidationManager;
    }

    function setAddresses(address _priceFeedAddress, address _sortedPositionsAddress, address _collateralToken) external {
        require(address(sortedPositions) == address(0));
        priceFeed = IPriceFeed(_priceFeedAddress);
        sortedPositions = ISortedPositions(_sortedPositionsAddress);
        collateralToken = IERC20(_collateralToken);

        systemDeploymentTime = block.timestamp;
        sunsetting = false;
        activeInterestIndex = INTEREST_PRECISION;
        lastActiveIndexUpdate = block.timestamp;
    }

    /**
     * @notice Sets the pause state for this position manager
     *         Pausing is used to mitigate risks in exceptional circumstances
     *         Functionalities affected by pausing are:
     *         - New borrowing is not possible
     *         - New collateral deposits are not possible
     * @param _paused If true the protocol is paused
     */
    function setPaused(bool _paused) external {
        require((_paused && msg.sender == guardian()) || msg.sender == owner(), "Unauthorized");
        paused = _paused;
    }

    /**
     * @notice Sets a custom price feed for this position manager
     * @param _priceFeedAddress Price feed address
     */
    function setPriceFeed(address _priceFeedAddress) external onlyOwner {
        priceFeed = IPriceFeed(_priceFeedAddress);
        emit PriceFeedUpdated(_priceFeedAddress);
    }

    function setCollVaultRouter(address _collVaultRouter) external onlyOwner {
        collVaultRouter = _collVaultRouter;
    }

    function setInterestReceiver(address _interestReceiver) external onlyOwner {
        interestReceiver = _interestReceiver;
        emit InterestReceiverSet(_interestReceiver);
    }

    /**
     * @notice Starts sunsetting a collateral
     *         During sunsetting only the following are possible:
               1) Disable collateral handoff to SP
               2) Greatly Increase interest rate to incentivize redemptions
               3) Remove redemptions fees
               4) Disable new loans
        @dev IMPORTANT: When sunsetting a collateral altogether this function should be called on
                        all PM linked to that collateral as well as `StabilityPool.startCollateralSunset`
        @dev IMPORTANT: A peripheral system will ensure users aren't MEVed due to redemptions fees being removed
     */
    function startSunset() external onlyOwner {
        sunsetting = true;
        _accrueActiveInterests();
        interestRate = SUNSETTING_INTEREST_RATE;
        // accrual function doesn't update timestamp if interest was 0
        lastActiveIndexUpdate = block.timestamp;
        redemptionFeeFloor = 0;
        maxSystemDebt = 0;
        baseRate = 0;
        maxRedemptionFee = 0;
    }

    /*
        _minuteDecayFactor is calculated as

            10**18 * (1/2)**(1/n)

        where n = the half-life in minutes
     */
    function setParameters(IFactory.DeploymentParams calldata params) public  {
        require(!sunsetting, "Cannot change after sunset");
        require(params.MCR >= 1e18, "MCR cannot < 100%");

        if (minuteDecayFactor != 0) {
            require(msg.sender == owner(), "Only owner");
        }
        require(
            params.minuteDecayFactor >= 977159968434245900 && // half-life of 30 minutes
                params.minuteDecayFactor <= 999931237762985000 // half-life of 1 week
        );
        require(params.redemptionFeeFloor <= params.maxRedemptionFee && params.maxRedemptionFee <= DECIMAL_PRECISION);
        require(params.borrowingFeeFloor <= params.maxBorrowingFee && params.maxBorrowingFee <= DECIMAL_PRECISION);

        _decayBaseRate();

        minuteDecayFactor = params.minuteDecayFactor;
        redemptionFeeFloor = params.redemptionFeeFloor;
        maxRedemptionFee = params.maxRedemptionFee;
        borrowingFeeFloor = params.borrowingFeeFloor;
        maxBorrowingFee = params.maxBorrowingFee;
        maxSystemDebt = params.maxDebt;
        collVaultRouter = params.collVaultRouter;

        uint256 newInterestRate = (INTEREST_PRECISION * params.interestRateInBps) / (BP * SECONDS_IN_YEAR);
        if (newInterestRate != interestRate) {
            _accrueActiveInterests();
            // accrual function doesn't update timestamp if interest was 0
            lastActiveIndexUpdate = block.timestamp;
            interestRate = newInterestRate;
        }
        MCR = params.MCR;

        emit NewParameters(params);
    }

    function collectInterests() external {
        _accrueActiveInterests();
        uint256 interestPayableCached = interestPayable;
        require(interestPayableCached > 0, "Nothing to collect");
        
        uint256 interestPayableToTreasury = Math.mulDiv(interestPayableCached, CORE.interestProtocolShare(), BP);
        uint256 interestPayableToProtocol = interestPayableCached - interestPayableToTreasury;
        address protocolReceiver = interestReceiver != address(0) ? interestReceiver : CORE.defaultInterestReceiver();

        if (interestPayableToTreasury != 0) debtToken.mint(CORE.feeReceiver(), interestPayableToTreasury);
        if (interestPayableToProtocol != 0) debtToken.mint(protocolReceiver, interestPayableToProtocol);

        interestPayable = 0;
    }

    // --- Getters ---

    function fetchPrice() public view returns (uint256) {
        IPriceFeed _priceFeed = priceFeed;
        if (address(_priceFeed) == address(0)) {
            _priceFeed = IPriceFeed(CORE.priceFeed());
        }
        return _priceFeed.fetchPrice(address(collateralToken));
    }

    function getPositionOwnersCount() external view returns (uint256) {
        return PositionOwners.length;
    }

    function getPositionFromPositionOwnersArray(uint256 _index) external view returns (address) {
        return PositionOwners[_index];
    }

    function getPositionStatus(address _borrower) external view returns (uint256) {
        return uint256(Positions[_borrower].status);
    }

    function getPositionStake(address _borrower) external view returns (uint256) {
        return Positions[_borrower].stake;
    }

    /**
        @notice Get the current total collateral and debt amounts for a position
        @dev Also includes pending rewards from redistribution
     */
    function getPositionCollAndDebt(address _borrower) public view returns (uint256 coll, uint256 debt) {
        (debt, coll, , ) = getEntireDebtAndColl(_borrower);
        return (coll, debt);
    }

    /**
        @notice Get the total and pending collateral and debt amounts for a position
        @dev Used by the liquidation manager
     */
    function getEntireDebtAndColl(
        address _borrower
    ) public view returns (uint256 debt, uint256 coll, uint256 pendingDebtReward, uint256 pendingCollateralReward) {
        Position storage t = Positions[_borrower];
        debt = t.debt;
        coll = t.coll;

        (pendingCollateralReward, pendingDebtReward) = getPendingCollAndDebtRewards(_borrower);
        // Accrued position interest for correct liquidation values. This assumes the index to be updated.
        uint256 positionInterestIndex = t.activeInterestIndex;
        if (positionInterestIndex > 0) {
            (uint256 currentIndex, ) = _calculateInterestIndex();
            debt = (debt * currentIndex) / positionInterestIndex;
        }

        debt = debt + pendingDebtReward;
        coll = coll + pendingCollateralReward;
    }

    function getEntireSystemColl() public view returns (uint256) {
        return totalActiveCollateral + defaultedCollateral;
    }

    function getEntireSystemDebt() public view returns (uint256) {
        uint256 currentActiveDebt = totalActiveDebt;
        (, uint256 interestFactor) = _calculateInterestIndex();
        if (interestFactor > 0) {
            uint256 activeInterests = Math.mulDiv(currentActiveDebt, interestFactor, INTEREST_PRECISION);
            currentActiveDebt = currentActiveDebt + activeInterests;
        }
        return currentActiveDebt + defaultedDebt;
    }

    function getEntireSystemBalances() external view returns (uint256, uint256, uint256) {
        return (getEntireSystemColl(), getEntireSystemDebt(), fetchPrice());
    }

    // --- Helper functions ---

    // Return the nominal collateral ratio (ICR) of a given Position, without the price. Takes a positions pending coll and debt rewards from redistributions into account.
    function getNominalICR(address _borrower) public view returns (uint256) {
        (uint256 currentCollateral, uint256 currentDebt) = getPositionCollAndDebt(_borrower);

        uint256 NICR = PropMath._computeNominalCR(currentCollateral, currentDebt);
        return NICR;
    }

    // Return the current collateral ratio (ICR) of a given Position. Takes a positions pending coll and debt rewards from redistributions into account.
    function getCurrentICR(address _borrower, uint256 _price) public view returns (uint256) {
        (uint256 currentCollateral, uint256 currentDebt) = getPositionCollAndDebt(_borrower);

        uint256 ICR = PropMath._computeCR(currentCollateral, currentDebt, _price);
        return ICR;
    }

    function getTotalActiveCollateral() public view returns (uint256) {
        return totalActiveCollateral;
    }

    function getTotalActiveDebt() public view returns (uint256) {
        uint256 currentActiveDebt = totalActiveDebt;
        (, uint256 interestFactor) = _calculateInterestIndex();
        if (interestFactor > 0) {
            uint256 activeInterests = Math.mulDiv(currentActiveDebt, interestFactor, INTEREST_PRECISION);
            currentActiveDebt = currentActiveDebt + activeInterests;
        }
        return currentActiveDebt;
    }

    // Get the borrower's pending accumulated collateral and debt rewards, earned by their stake
    function getPendingCollAndDebtRewards(address _borrower) public view returns (uint256, uint256) {
        RewardSnapshot memory snapshot = rewardSnapshots[_borrower];

        uint256 coll = L_collateral - snapshot.collateral;
        uint256 debt = L_debt - snapshot.debt;

        if (coll + debt == 0 || Positions[_borrower].status != Status.active) return (0, 0);

        uint256 stake = Positions[_borrower].stake;
        return ((stake * coll) / DECIMAL_PRECISION, (stake * debt) / DECIMAL_PRECISION);
    }

    function hasPendingRewards(address _borrower) public view returns (bool) {
        /*
         * A Position 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 (Positions[_borrower].status != Status.active) {
            return false;
        }

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

    // --- 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 debt borrowing operation.
     * then,
     * 2) increases the baseRate based on the amount redeemed, as a proportion of total supply
     */
    function _updateBaseRateFromRedemption(
        uint256 _collateralDrawn,
        uint256 _price,
        uint256 _totalDebtSupply
    ) internal returns (uint256) {
        uint256 decayedBaseRate = _calcDecayedBaseRate();

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

        uint256 newBaseRate = decayedBaseRate + (redeemedDebtFraction / BETA);
        newBaseRate = PropMath._min(newBaseRate, DECIMAL_PRECISION); // cap baseRate at a maximum of 100%

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

        _updateLastFeeOpTime();

        return newBaseRate;
    }

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

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

    function _calcRedemptionRate(uint256 _baseRate) internal view returns (uint256) {
        return
            PropMath._min(
                redemptionFeeFloor + _baseRate,
                maxRedemptionFee
            );
    }

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

    function _calcRedemptionFee(uint256 _redemptionRate, uint256 _collateralDrawn) internal pure returns (uint256) {
        uint256 redemptionFee = (_redemptionRate * _collateralDrawn) / DECIMAL_PRECISION;
        require(redemptionFee < _collateralDrawn, "Fee exceeds returned collateral");
        return redemptionFee;
    }

    // --- Borrowing fee functions ---

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

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

    function _calcBorrowingRate(uint256 _baseRate) internal view returns (uint256) {
        return PropMath._min(borrowingFeeFloor + _baseRate, maxBorrowingFee);
    }

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

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

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

    // --- 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 >= SECONDS_IN_ONE_MINUTE) {
            lastFeeOperationTime += _minutesPassedSinceLastFeeOp() * SECONDS_IN_ONE_MINUTE;
            emit LastFeeOpTimeUpdated(block.timestamp);
        }
    }

    function _calcDecayedBaseRate() internal view returns (uint256) {
        uint256 minutesPassed = _minutesPassedSinceLastFeeOp();
        uint256 decayFactor = PropMath._decPow(minuteDecayFactor, minutesPassed);

        return (baseRate * decayFactor) / DECIMAL_PRECISION;
    }

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

    // --- Redemption functions ---

    /* Send _debtAmount debt to the system and redeem the corresponding amount of collateral from as many Positions as are needed to fill the redemption
     * request.  Applies pending rewards to a Position before reducing its debt and coll.
     *
     * Note that if _amount is very large, this function can run out of gas, specially if traversed positions 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 Positions 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 position 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 Positions 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 Position 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 Position will be after redemption, and pass a hint for its position
     * in the sortedPositions 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 Position 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 Position and the sender will keep the remaining debt amount, which they can attempt
     * to redeem later.
     */
    function redeemCollateral(
        uint256 _debtAmount,
        address _firstRedemptionHint,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintNICR,
        uint256 _maxIterations,
        uint256 _maxFeePercentage
    ) external {
        ISortedPositions _sortedPositionsCached = sortedPositions;
        RedemptionTotals memory totals;

        require(
            _maxFeePercentage >= redemptionFeeFloor && _maxFeePercentage <= maxRedemptionFee,
            "Max fee not in bounds"
        );
        require(block.timestamp >= systemDeploymentTime + CORE.dmBootstrapPeriod(), "BOOTSTRAP_PERIOD");
        totals.price = fetchPrice();
        require(IBorrowerOperations(borrowerOperations).getTCR() >= MCR, "Cannot redeem when TCR < MCR");
        require(_debtAmount > 0, "Amount must be greater than zero");
        require(debtToken.balanceOf(msg.sender) >= _debtAmount, "Insufficient balance");
        _updateBalances();
        totals.totalDebtSupplyAtStart = getEntireSystemDebt();

        totals.remainingDebt = _debtAmount;
        address currentBorrower;

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

        // Loop through the Positions starting from the one with lowest collateral ratio until _amount of debt is exchanged for collateral
        if (_maxIterations == 0) {
            _maxIterations = 100;
        }
        while (currentBorrower != address(0) && totals.remainingDebt > 0 && _maxIterations > 0) {
            _maxIterations--;
            // Save the address of the Position preceding the current one, before potentially modifying the list
            address nextUserToCheck = _sortedPositionsCached.getPrev(currentBorrower);

            _applyPendingRewards(currentBorrower);
            SingleRedemptionValues memory singleRedemption = _redeemCollateralFromPosition(
                _sortedPositionsCached,
                currentBorrower,
                totals.remainingDebt,
                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 Position

            totals.totalDebtToRedeem = totals.totalDebtToRedeem + singleRedemption.debtLot;
            totals.totalCollateralDrawn = totals.totalCollateralDrawn + singleRedemption.collateralLot;

            totals.remainingDebt = totals.remainingDebt - singleRedemption.debtLot;
            currentBorrower = nextUserToCheck;
        }
        require(totals.totalCollateralDrawn > 0, "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 debt supply value, from before it was reduced by the redemption.
        // only callabe when not sunsetting
        if (!sunsetting) {
            _updateBaseRateFromRedemption(totals.totalCollateralDrawn, totals.price, totals.totalDebtSupplyAtStart);
        }

        // Calculate the collateral fee
        totals.collateralFee = sunsetting ? 0 : _calcRedemptionFee(getRedemptionRate(), totals.totalCollateralDrawn);

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

        _sendCollateral(CORE.feeReceiver(), totals.collateralFee);

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

        emit Redemption(msg.sender, _debtAmount, totals.totalDebtToRedeem, totals.totalCollateralDrawn, totals.collateralFee);

        // Burn the total debt that is cancelled with debt, and send the redeemed collateral to msg.sender
        debtToken.burn(msg.sender, totals.totalDebtToRedeem);
        // Update Position Manager debt, and send collateral to account
        totalActiveDebt = totalActiveDebt - totals.totalDebtToRedeem;
        _sendCollateral(msg.sender, totals.collateralToSendToRedeemer);
        _resetState();
    }

    // Redeem as much collateral as possible from _borrower's Position in exchange for debt up to _maxDebtAmount
    function _redeemCollateralFromPosition(
        ISortedPositions _sortedPositionsCached,
        address _borrower,
        uint256 _maxDebtAmount,
        uint256 _price,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintNICR
    ) internal returns (SingleRedemptionValues memory singleRedemption) {
        Position storage t = Positions[_borrower];
        // Determine the remaining amount (lot) to be redeemed, capped by the entire debt of the Position minus the liquidation reserve
        singleRedemption.debtLot = PropMath._min(_maxDebtAmount, t.debt - DEBT_GAS_COMPENSATION);

        // Get the CollateralLot of equivalent value in USD
        singleRedemption.collateralLot = (singleRedemption.debtLot * DECIMAL_PRECISION) / _price;

        // Decrease the debt and collateral of the current Position according to the debt lot and corresponding collateral to send
        uint256 newDebt = (t.debt) - singleRedemption.debtLot;
        uint256 newColl = (t.coll) - singleRedemption.collateralLot;
        if (newDebt == DEBT_GAS_COMPENSATION) {
            // No debt left in the Position (except for the liquidation reserve), therefore the position gets closed
            _removeStake(_borrower);
            _closePosition(_borrower, Status.closedByRedemption);
            _redeemClosePosition(_borrower, DEBT_GAS_COMPENSATION, newColl);
            emit PositionUpdated(_borrower, 0, 0, 0, PositionManagerOperation.redeemCollateral);
        } else {
            uint256 newNICR = PropMath._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.
             */

            {
                // We check if the ICR hint is reasonable up to date, with continuous interest there might be slight differences (<1bps)
                uint256 icrError = _partialRedemptionHintNICR > newNICR
                    ? _partialRedemptionHintNICR - newNICR
                    : newNICR - _partialRedemptionHintNICR;
                if (
                    icrError > 5e14 ||
                    _getNetDebt(newDebt) < IBorrowerOperations(borrowerOperations).minNetDebt()
                ) {
                    singleRedemption.cancelledPartial = true;
                    return singleRedemption;
                }
            }

            _sortedPositionsCached.reInsert(_borrower, newNICR, _upperPartialRedemptionHint, _lowerPartialRedemptionHint);

            t.debt = newDebt;
            t.coll = newColl;
            _updateStakeAndTotalStakes(t);

            emit PositionUpdated(_borrower, newDebt, newColl, t.stake, PositionManagerOperation.redeemCollateral);
        }
        return singleRedemption;
    }

    /*
     * Called when a full redemption occurs, and closes the position.
     * The redeemer swaps (debt - liquidation reserve) debt for (debt - liquidation reserve) worth of collateral, so the debt liquidation reserve left corresponds to the remaining debt.
     * In order to close the position, the debt liquidation reserve is burned, and the corresponding debt is removed.
     * The debt recorded on the positions struct is zero'd elswhere, in _closePosition.
     * Any surplus collateral left in the position can be later claimed by the borrower.
     */
    function _redeemClosePosition(address _borrower, uint256 _debt, uint256 _collateral) internal {
        debtToken.burn(gasPoolAddress, _debt);
        totalActiveDebt = totalActiveDebt - _debt;

        surplusBalances[_borrower] += _collateral;
        totalActiveCollateral -= _collateral;
    }

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

        address nextPosition = _sortedPositions.getNext(_firstRedemptionHint);
        return nextPosition == address(0) || getCurrentICR(nextPosition, _price) < _100pct;
    }

    /**
     * Claim remaining collateral from a redemption or from a liquidation with ICR > MCR in Recovery Mode
     */
    function claimCollateral(address borrower, address _receiver) external {
        if (msg.sender != collVaultRouter) {
            borrower = msg.sender;
        }
        uint256 claimableColl = surplusBalances[borrower];
        require(claimableColl > 0, "No collateral available to claim");

        surplusBalances[borrower] = 0;

        collateralToken.safeTransfer(_receiver, claimableColl);
    }

    // --- Position Adjustment functions ---

    function openPosition(
        address _borrower,
        uint256 _collateralAmount,
        uint256 _compositeDebt,
        uint256 NICR,
        address _upperHint,
        address _lowerHint
    ) external whenNotPaused returns (uint256 stake, uint256 arrayIndex) {
        _requireCallerIsBO();
        require(!sunsetting, "Cannot open while sunsetting");
        _isPermissionedCheck(_borrower);

        Position storage t = Positions[_borrower];
        require(t.status != Status.active, "BorrowerOps: Position is active");
        t.status = Status.active;
        t.coll = _collateralAmount;
        t.debt = _compositeDebt;
        uint256 currentInterestIndex = _accrueActiveInterests();
        t.activeInterestIndex = currentInterestIndex;
        _updatePositionRewardSnapshots(_borrower);
        stake = _updateStakeAndTotalStakes(t);
        sortedPositions.insert(_borrower, NICR, _upperHint, _lowerHint);

        PositionOwners.push(_borrower);
        arrayIndex = PositionOwners.length - 1;
        t.arrayIndex = uint128(arrayIndex);

        totalActiveCollateral = totalActiveCollateral + _collateralAmount;
        uint256 _newTotalDebt = totalActiveDebt + _compositeDebt;
        require(_newTotalDebt + defaultedDebt <= maxSystemDebt, "Collateral debt limit reached");
        totalActiveDebt = _newTotalDebt;
    }

    function updatePositionFromAdjustment(
        bool _isDebtIncrease,
        uint256 _debtChange,
        uint256 _netDebtChange,
        bool _isCollIncrease,
        uint256 _collChange,
        address _upperHint,
        address _lowerHint,
        address _borrower,
        address _receiver
    ) external returns (uint256, uint256, uint256) {
        _requireCallerIsBO();
        if (_isCollIncrease || _isDebtIncrease) {
            require(!paused, "Collateral Paused");
            require(!sunsetting, "Cannot increase while sunsetting");
        }

        Position storage t = Positions[_borrower];
        require(t.status == Status.active, "Position closed or does not exist");

        uint256 newDebt = t.debt;
        if (_debtChange > 0) {
            if (_isDebtIncrease) {
                newDebt = newDebt + _netDebtChange;
                _increaseDebt(_receiver, _netDebtChange, _debtChange);
            } else {
                newDebt = newDebt - _netDebtChange;
                _decreaseDebt(_receiver, _debtChange);
            }
            t.debt = newDebt;
        }

        uint256 newColl = t.coll;
        if (_collChange > 0) {
            if (_isCollIncrease) {
                newColl = newColl + _collChange;
                totalActiveCollateral = totalActiveCollateral + _collChange;
                // trust that BorrowerOperations sent the collateral
            } else {
                newColl = newColl - _collChange;
                _sendCollateral(_receiver, _collChange);
            }
            t.coll = newColl;
        }

        uint256 newNICR = PropMath._computeNominalCR(newColl, newDebt);
        sortedPositions.reInsert(_borrower, newNICR, _upperHint, _lowerHint);

        return (newColl, newDebt, _updateStakeAndTotalStakes(t));
    }

    function closePosition(address _borrower, address _receiver, uint256 collAmount, uint256 debtAmount) external {
        _requireCallerIsBO();
        require(Positions[_borrower].status == Status.active, "Position closed or does not exist");
        _removeStake(_borrower);
        _closePosition(_borrower, Status.closedByOwner);
        totalActiveDebt = totalActiveDebt - debtAmount;
        _sendCollateral(_receiver, collAmount);
        _resetState();
    }

    /**
        @dev Only called from `closePosition` because liquidating the final position is blocked in
             `LiquidationManager`. Many liquidation paths involve redistributing debt and
             collateral to existing positions. If the collateral is being sunset, the final position
             must be closed by repaying the debt or via a redemption.
     */
    function _resetState() private {
        if (PositionOwners.length == 0) {
            activeInterestIndex = INTEREST_PRECISION;
            lastActiveIndexUpdate = block.timestamp;
            totalStakes = 0;
            totalStakesSnapshot = 0;
            totalCollateralSnapshot = 0;
            L_collateral = 0;
            L_debt = 0;
            lastCollateralError_Redistribution = 0;
            lastDebtError_Redistribution = 0;
            totalActiveCollateral = 0;
            totalActiveDebt = 0;
            defaultedCollateral = 0;
            defaultedDebt = 0;
        }
    }

    function _closePosition(address _borrower, Status closedStatus) internal {
        uint256 PositionOwnersArrayLength = PositionOwners.length;

        Position storage t = Positions[_borrower];
        t.status = closedStatus;
        t.coll = 0;
        t.debt = 0;
        t.activeInterestIndex = 0;
        ISortedPositions sortedPositionsCached = sortedPositions;
        rewardSnapshots[_borrower].collateral = 0;
        rewardSnapshots[_borrower].debt = 0;
        if (PositionOwnersArrayLength > 1 && sortedPositionsCached.getSize() > 1) {
            // remove position owner from the PositionOwners array, not preserving array order
            uint128 index = t.arrayIndex;
            address addressToMove = PositionOwners[PositionOwnersArrayLength - 1];
            PositionOwners[index] = addressToMove;
            Positions[addressToMove].arrayIndex = index;
            emit PositionIndexUpdated(addressToMove, index);
        }

        PositionOwners.pop();

        sortedPositionsCached.remove(_borrower);
        t.arrayIndex = 0;
    }

    // Updates the baseRate state variable based on time elapsed since the last redemption or debt borrowing operation.
    function decayBaseRateAndGetBorrowingFee(uint256 _debt) external returns (uint256) {
        _requireCallerIsBO();
        uint256 rate = _decayBaseRate();

        return _calcBorrowingFee(_calcBorrowingRate(rate), _debt);
    }

    function _decayBaseRate() internal returns (uint256) {
        uint256 decayedBaseRate = _calcDecayedBaseRate();

        baseRate = decayedBaseRate;
        emit BaseRateUpdated(decayedBaseRate);

        _updateLastFeeOpTime();

        return decayedBaseRate;
    }

    function applyPendingRewards(address _borrower) external returns (uint256 coll, uint256 debt) {
        _requireCallerIsBO();
        return _applyPendingRewards(_borrower);
    }

    // Add the borrowers's coll and debt rewards earned from redistributions, to their Position
    function _applyPendingRewards(address _borrower) internal returns (uint256 coll, uint256 debt) {
        Position storage t = Positions[_borrower];
        if (t.status == Status.active) {
            uint256 positionInterestIndex = t.activeInterestIndex;
            uint256 currentInterestIndex = _accrueActiveInterests();
            debt = t.debt;
            uint256 prevDebt = debt;
            coll = t.coll;
            // We accrue interests for this position if not already updated
            if (positionInterestIndex < currentInterestIndex) {
                debt = (debt * currentInterestIndex) / positionInterestIndex;
                t.activeInterestIndex = currentInterestIndex;
            }

            if (rewardSnapshots[_borrower].collateral < L_collateral) {
                // Compute pending rewards
                (uint256 pendingCollateralReward, uint256 pendingDebtReward) = getPendingCollAndDebtRewards(_borrower);

                // Apply pending rewards to positions state
                coll = coll + pendingCollateralReward;
                t.coll = coll;
                debt = debt + pendingDebtReward;

                _updatePositionRewardSnapshots(_borrower);

                _movePendingPositionRewardsToActiveBalance(pendingDebtReward, pendingCollateralReward);

                emit PositionUpdated(_borrower, debt, coll, t.stake, PositionManagerOperation.applyPendingRewards);
            }
            if (prevDebt != debt) {
                t.debt = debt;
            }
        }
        return (coll, debt);
    }

    function _updatePositionRewardSnapshots(address _borrower) internal {
        uint256 L_collateralCached = L_collateral;
        uint256 L_debtCached = L_debt;
        rewardSnapshots[_borrower] = RewardSnapshot(L_collateralCached, L_debtCached);
        emit PositionSnapshotsUpdated(L_collateralCached, L_debtCached);
    }

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

    // Update borrower's stake based on their latest collateral value
    function _updateStakeAndTotalStakes(Position storage t) internal returns (uint256) {
        uint256 newStake = _computeNewStake(t.coll);
        uint256 oldStake = t.stake;
        t.stake = newStake;
        uint256 newTotalStakes = totalStakes - oldStake + newStake;
        totalStakes = newTotalStakes;
        emit TotalStakesUpdated(newTotalStakes);

        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 (uint256) {
        uint256 stake;
        uint256 totalCollateralSnapshotCached = totalCollateralSnapshot;
        if (totalCollateralSnapshotCached == 0) {
            stake = _coll;
        } else {
            /*
             * The following assert() holds true because:
             * - The system always contains >= 1 position
             * - When we close or liquidate a position, we redistribute the pending rewards, so if all positions were closed/liquidated,
             * rewards would’ve been emptied and totalCollateralSnapshot would be zero too.
             */
            uint256 totalStakesSnapshotCached = totalStakesSnapshot;
            assert(totalStakesSnapshotCached > 0);
            stake = (_coll * totalStakesSnapshotCached) / totalCollateralSnapshotCached;
        }
        return stake;
    }

    // --- Liquidation Functions ---

    function closePositionByLiquidation(address _borrower) external {
        _requireCallerIsLM();
        _removeStake(_borrower);
        _closePosition(_borrower, Status.closedByLiquidation);
    }

    function movePendingPositionRewardsToActiveBalances(uint256 _debt, uint256 _collateral) external {
        _requireCallerIsLM();
        _movePendingPositionRewardsToActiveBalance(_debt, _collateral);
    }

    function _movePendingPositionRewardsToActiveBalance(uint256 _debt, uint256 _collateral) internal {
        defaultedDebt -= _debt;
        totalActiveDebt += _debt;
        defaultedCollateral -= _collateral;
        totalActiveCollateral += _collateral;
    }

    function addCollateralSurplus(address borrower, uint256 collSurplus) external {
        _requireCallerIsLM();
        surplusBalances[borrower] += collSurplus;
    }

    function finalizeLiquidation(
        address _liquidator,
        uint256 _debt,
        uint256 _coll,
        uint256 _collSurplus,
        uint256 _debtGasComp,
        uint256 _collGasComp
    ) external {
        _requireCallerIsLM();
        // redistribute debt and collateral
        _redistributeDebtAndColl(_debt, _coll);
        uint256 _activeColl = totalActiveCollateral;
        if (_collSurplus > 0) {
            _activeColl -= _collSurplus;
            totalActiveCollateral = _activeColl;
        }
        // update system snapshot
        totalStakesSnapshot = totalStakes;
        totalCollateralSnapshot = _activeColl + defaultedCollateral - _collGasComp;  
        emit SystemSnapshotsUpdated(totalStakesSnapshot, totalCollateralSnapshot);
        
        // Send collateral and debt compensation to liquidator
        debtToken.returnFromPool(gasPoolAddress, _liquidator, _debtGasComp);

        _sendCollateral(_liquidator, _collGasComp);
    }

    function _redistributeDebtAndColl(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_debt:
         *
         * 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 positionominator, 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 debtNumerator = (_debt * DECIMAL_PRECISION) + lastDebtError_Redistribution;
        uint256 totalStakesCached = totalStakes;
        // Get the per-unit-staked terms
        uint256 collateralRewardPerUnitStaked = collateralNumerator / totalStakesCached;
        uint256 debtRewardPerUnitStaked = debtNumerator / totalStakesCached;

        lastCollateralError_Redistribution = collateralNumerator - (collateralRewardPerUnitStaked * totalStakesCached);
        lastDebtError_Redistribution = debtNumerator - (debtRewardPerUnitStaked * totalStakesCached);

        // Add per-unit-staked terms to the running totals
        uint256 new_L_collateral = L_collateral + collateralRewardPerUnitStaked;
        uint256 new_L_debt = L_debt + debtRewardPerUnitStaked;
        L_collateral = new_L_collateral;
        L_debt = new_L_debt;

        emit LTermsUpdated(new_L_collateral, new_L_debt);

        totalActiveDebt -= _debt;
        defaultedDebt += _debt;
        defaultedCollateral += _coll;
        totalActiveCollateral -= _coll;
    }

    // --- Position property setters ---

    function _sendCollateral(address _account, uint256 _amount) private {
        if (_amount > 0) {
            totalActiveCollateral = totalActiveCollateral - _amount;
            emit CollateralSent(_account, _amount);

            collateralToken.safeTransfer(_account, _amount);
        }
    }

    function _increaseDebt(address account, uint256 netDebtAmount, uint256 debtAmount) internal {
        uint256 _newTotalDebt = totalActiveDebt + netDebtAmount;
        require(_newTotalDebt + defaultedDebt <= maxSystemDebt, "Collateral debt limit reached");
        totalActiveDebt = _newTotalDebt;
        debtToken.mint(account, debtAmount);
    }

    function decreaseDebtAndSendCollateral(address account, uint256 debt, uint256 coll) external {
        _requireCallerIsLM();
        _decreaseDebt(account, debt);
        _sendCollateral(account, coll);
    }

    function _decreaseDebt(address account, uint256 amount) internal {
        debtToken.burn(account, amount);
        totalActiveDebt = totalActiveDebt - amount;
    }

    // --- Balances and interest ---

    function updateBalances() external {
        _requireCallerIsLM();
        _updateBalances();
    }

    function _updateBalances() private {
        _accrueActiveInterests();
    }

    // This function must be called any time the debt or the interest changes
    function _accrueActiveInterests() internal returns (uint256) {
        (uint256 currentInterestIndex, uint256 interestFactor) = _calculateInterestIndex();
        if (interestFactor > 0) {
            uint256 currentDebt = totalActiveDebt;
            uint256 activeInterests = Math.mulDiv(currentDebt, interestFactor, INTEREST_PRECISION);
            totalActiveDebt = currentDebt + activeInterests;
            interestPayable = interestPayable + activeInterests;
            activeInterestIndex = currentInterestIndex;
            lastActiveIndexUpdate = block.timestamp;
        }
        return currentInterestIndex;
    }

    function _calculateInterestIndex() internal view returns (uint256 currentInterestIndex, uint256 interestFactor) {
        uint256 lastIndexUpdateCached = lastActiveIndexUpdate;
        // Short circuit if we updated in the current block
        if (lastIndexUpdateCached == block.timestamp) return (activeInterestIndex, 0);
        uint256 currentInterest = interestRate;
        currentInterestIndex = activeInterestIndex; // we need to return this if it's already up to date
        if (currentInterest > 0) {
            /*
             * Calculate the interest accumulated and the new index:
             * We compound the index and increase the debt accordingly
             */
            uint256 deltaT = block.timestamp - lastIndexUpdateCached;
            interestFactor = deltaT * currentInterest;
            currentInterestIndex =
                currentInterestIndex +
                Math.mulDiv(currentInterestIndex, interestFactor, INTEREST_PRECISION);
        }
    }

    // --- Requires ---

    function _requireCallerIsBO() internal view {
        require(msg.sender == borrowerOperations, "Caller not BO");
    }

    function _requireCallerIsLM() internal view {
        require(msg.sender == liquidationManager, "Not Liquidation Manager");
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 5 of 19 : IERC3156FlashBorrower.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC3156 FlashBorrower, as defined in
 * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
 *
 * _Available since v4.1._
 */
interface IERC3156FlashBorrower {
    /**
     * @dev Receive a flash loan.
     * @param initiator The initiator of the loan.
     * @param token The loan currency.
     * @param amount The amount of tokens lent.
     * @param fee The additional amount of tokens to repay.
     * @param data Arbitrary data structure, intended to contain user-defined parameters.
     * @return The keccak256 hash of "IERC3156FlashBorrower.onFlashLoan"
     */
    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external returns (bytes32);
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

import {ICore} from "./ICore.sol";

interface IBorrowerOperations {
    struct Balances {
        uint256[] collaterals;
        uint256[] debts;
        uint256[] prices;
    }

    event BorrowingFeePaid(address indexed borrower, uint256 amount);
    event CollateralConfigured(address positionManager, address collateralToken);
    event PositionCreated(address indexed _borrower, uint256 arrayIndex);
    event PositionManagerRemoved(address positionManager);
    event PositionUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 stake, uint8 operation);

    function addColl(
        address positionManager,
        address account,
        uint256 _collateralAmount,
        address _upperHint,
        address _lowerHint
    ) external;

    function adjustPosition(
        address positionManager,
        address account,
        uint256 _maxFeePercentage,
        uint256 _collDeposit,
        uint256 _collWithdrawal,
        uint256 _debtChange,
        bool _isDebtIncrease,
        address _upperHint,
        address _lowerHint
    ) external;

    function closePosition(address positionManager, address account) external;

    function configureCollateral(address positionManager, address collateralToken) external;

    function fetchBalances() external view returns (Balances memory balances);

    function getGlobalSystemBalances() external view returns (uint256 totalPricedCollateral, uint256 totalDebt);

    function getTCR() external view returns (uint256 globalTotalCollateralRatio);

    function openPosition(
        address positionManager,
        address account,
        uint256 _maxFeePercentage,
        uint256 _collateralAmount,
        uint256 _debtAmount,
        address _upperHint,
        address _lowerHint
    ) external;

    function removePositionManager(address positionManager) external;

    function repayDebt(
        address positionManager,
        address account,
        uint256 _debtAmount,
        address _upperHint,
        address _lowerHint
    ) external;

    function setDelegateApproval(address _delegate, bool _isApproved) external;

    function setMinNetDebt(uint256 _minNetDebt) external;

    function withdrawColl(
        address positionManager,
        address account,
        uint256 _collWithdrawal,
        address _upperHint,
        address _lowerHint
    ) external;

    function withdrawDebt(
        address positionManager,
        address account,
        uint256 _maxFeePercentage,
        uint256 _debtAmount,
        address _upperHint,
        address _lowerHint
    ) external;

    function positionManagers(uint256) external view returns (address);

    function checkRecoveryMode(uint256 TCR) external view returns (bool);

    function DEBT_GAS_COMPENSATION() external view returns (uint256);

    function DECIMAL_PRECISION() external view returns (uint256);

    function PERCENT_DIVISOR() external view returns (uint256);

    function CORE() external view returns (ICore);

    function debtToken() external view returns (address);

    function factory() external view returns (address);

    function getCompositeDebt(uint256 _debt) external view returns (uint256);

    function guardian() external view returns (address);

    function isApprovedDelegate(address owner, address caller) external view returns (bool isApproved);

    function minNetDebt() external view returns (uint256);

    function owner() external view returns (address);

    function positionManagersData(address) external view returns (address collateralToken, uint16 index);
}

File 7 of 19 : IDebtToken.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 
import { IERC3156FlashBorrower } from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
import "./ICore.sol";

interface IDebtToken is IERC20 {
    // --- Events ---
    event FlashLoanFeeUpdated(uint256 newFee);

    // --- Public constants ---
    function version() external view returns (string memory);
    function permitTypeHash() external view returns (bytes32);

    // --- Public immutables ---
    function gasPool() external view returns (address);
    function DEBT_GAS_COMPENSATION() external view returns (uint256);

    // --- Public mappings ---
    function liquidStabilityPools(address) external view returns (bool);
    function borrowerOperations(address) external view returns (bool);
    function factories(address) external view returns (bool);
    function peripheries(address) external view returns (bool);
    function positionManagers(address) external view returns (bool);

    // --- External functions ---

    function enablePositionManager(address _positionManager) external;
    function mintWithGasCompensation(address _account, uint256 _amount) external returns (bool);
    function burnWithGasCompensation(address _account, uint256 _amount) external returns (bool);
    function mint(address _account, uint256 _amount) external;
    function burn(address _account, uint256 _amount) external;
    function decimals() external view returns (uint8);
    function sendToPeriphery(address _sender, uint256 _amount) external;
    function sendToSP(address _sender, uint256 _amount) external;
    function returnFromPool(address _poolAddress, address _receiver, uint256 _amount) external;
    function transfer(address recipient, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function maxFlashLoan(address token) external view returns (uint256);
    function flashFee(address token, uint256 amount) external view returns (uint256);
    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) external returns (bool);
    function whitelistLiquidStabilityPoolAddress(address _liquidStabilityPool, bool active) external;
    function whitelistBorrowerOperationsAddress(address _borrowerOperations, bool active) external;
    function whitelistFactoryAddress(address _factory, bool active) external;
    function whitelistPeripheryAddress(address _periphery, bool active) external;
    function whitelistPSM(address, bool) external;
    function setDebtGasCompensation(uint256 _gasCompensation, bool _isFinalValue) external;
    function setFlashLoanFee(uint256 _fee) external;
    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function permit(
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
    function nonces(address owner) external view returns (uint256);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface ISortedPositions {
    event NodeAdded(address _id, uint256 _NICR);
    event NodeRemoved(address _id);

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

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

    function remove(address _id) external;

    function setAddresses(address _positionManagerAddress) external;

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

    function data() external view returns (address head, address tail, uint256 size);

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

    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 getSize() external view returns (uint256);

    function isEmpty() external view returns (bool);

    function positionManager() external view returns (address);

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IPriceFeed {
    struct FeedType {
        address spotOracle;
        bool isCollVault;
    }

    event NewOracleRegistered(address token, address chainlinkAggregator, address underlyingDerivative);
    event PriceFeedStatusUpdated(address token, address oracle, bool isWorking);
    event PriceRecordUpdated(address indexed token, uint256 _price);
    event NewCollVaultRegistered(address collVault, bool enable);
    event NewSpotOracleRegistered(address token, address spotOracle);

    function fetchPrice(address _token) external view returns (uint256);

    function getMultiplePrices(address[] memory _tokens) external view returns (uint256[] memory prices);

    function setOracle(
        address _token,
        address _chainlinkOracle,
        uint32 _heartbeat,
        uint16 _staleThreshold,
        address underlyingDerivative
    ) external;

    function whitelistCollateralVault(address _collateralVaultShareToken, bool enable) external;
    
    function setSpotOracle(address _token, address _spotOracle) external;
    
    function MAX_PRICE_DEVIATION_FROM_PREVIOUS_ROUND() external view returns (uint256);

    function CORE() external view returns (address);

    function RESPONSE_TIMEOUT() external view returns (uint256);

    function TARGET_DIGITS() external view returns (uint256);

    function guardian() external view returns (address);

    function oracleRecords(
        address
    )
        external
        view
        returns (
        address chainLinkOracle,
        uint8 decimals,
        uint32 heartbeat,
        uint16 staleThreshold,
        address underlyingDerivative
    );

    function isCollVault(address _collateralVaultShareToken) external view returns (bool);

    function isStableBPT(address _oracle) external view returns (bool);

    function isWeightedBPT(address _oracle) external view returns (bool);

    function getSpotOracle(address _token) external view returns (address);

    function feedType(address _token) external view returns (FeedType memory);

    function owner() external view returns (address);
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

interface IFactory {
    // commented values are suggested default parameters
    struct DeploymentParams {
        uint256 minuteDecayFactor; // 999037758833783000  (half life of 12 hours)
        uint256 redemptionFeeFloor; // 1e18 / 1000 * 5  (0.5%)
        uint256 maxRedemptionFee; // 1e18  (100%)
        uint256 borrowingFeeFloor; // 1e18 / 1000 * 5  (0.5%)
        uint256 maxBorrowingFee; // 1e18 / 100 * 5  (5%)
        uint256 interestRateInBps; // 100 (1%)
        uint256 maxDebt;
        uint256 MCR; // 12 * 1e17  (120%)
        address collVaultRouter; // set to address(0) if PositionManager coll is not CollateralVault
    }

    event NewDeployment(address collateral, address priceFeed, address positionManager, address sortedPositions);

    function deployNewInstance(
        address collateral,
        address priceFeed,
        address customPositionManagerImpl,
        address customSortedPositionsImpl,
        DeploymentParams calldata params,
        uint64 unlockRatePerSecond,
        bool forceThroughLspBalanceCheck
    ) external;

    function setImplementations(address _positionManagerImpl, address _sortedPositionsImpl) external;

    function CORE() external view returns (address);

    function borrowerOperations() external view returns (address);

    function debtToken() external view returns (address);

    function guardian() external view returns (address);

    function liquidationManager() external view returns (address);

    function owner() external view returns (address);

    function sortedPositionsImpl() external view returns (address);

    function liquidStabilityPool() external view returns (address);

    function positionManagerCount() external view returns (uint256);

    function positionManagerImpl() external view returns (address);

    function positionManagers(uint256) external view returns (address);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface ILiquidationManager {
    function batchLiquidatePositions(address positionManager, address[] calldata _positionArray, address liquidator) external;

    function enablePositionManager(address _positionManager) external;

    function liquidate(address positionManager, address borrower, address liquidator) external;

    function liquidatePositions(address positionManager, uint256 maxPositionsToLiquidate, uint256 maxICR, address liquidator) external;

    function DEBT_GAS_COMPENSATION() external view returns (uint256);

    function DECIMAL_PRECISION() external view returns (uint256);

    function PERCENT_DIVISOR() external view returns (uint256);

    function borrowerOperations() external view returns (address);

    function factory() external view returns (address);

    function liquidStabilityPool() external view returns (address);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 12 of 19 : SystemStart.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

import "../interfaces/core/ICore.sol";

/**
    @title System Start Time
    @dev Provides a unified `startTime` and `getWeek`, used for emissions.
 */
contract SystemStart {
    uint256 immutable startTime;

    constructor(address core) {
        startTime = ICore(core).startTime();
    }

    function getWeek() public view returns (uint256 week) {
        return (block.timestamp - startTime) / 1 weeks;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

/*
 * Base contract for PositionManager, BorrowerOperations and StabilityPool. Contains global system constants and
 * common functions.
 */
contract PropBase {
    uint256 public constant DECIMAL_PRECISION = 1e18;

    // Amount of debt to be locked in gas pool on opening positions
    uint256 public immutable DEBT_GAS_COMPENSATION;

    uint256 public constant PERCENT_DIVISOR = 1000; // dividing by 1000 yields 0.1%

    constructor(uint256 _gasCompensation) {
        DEBT_GAS_COMPENSATION = _gasCompensation;
    }

    // --- Gas compensation functions ---

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

    function _getNetDebt(uint256 _debt) internal view returns (uint256) {
        return _debt - DEBT_GAS_COMPENSATION;
    }

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

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

// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

library PropMath {
    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,
     * 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 (uint256) {
        return (_a < _b) ? _a : _b;
    }

    function _max(uint256 _a, uint256 _b) internal pure returns (uint256) {
        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 two functions that represent time in units of minutes:
     * 1) PositionManager._calcDecayedBaseRate
     * 2) CommunityIssuance._getCumulativeIssuanceFraction
     *
     * The exponent is capped to avoid reverting due to overflow. The cap 525600000 equals
     * "minutes in 1000 years": 60 * 24 * 365 * 1000
     *
     * If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be
     * negligibly different from just passing the cap, since:
     *
     * In function 1), the decayed base rate will be 0 for 1000 years or > 1000 years
     * In function 2), the difference in tokens issued at 1000 years and any time > 1000 years, will be negligible
     */
    function _decPow(uint256 _base, uint256 _minutes) internal pure returns (uint256) {
        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 (uint256) {
        return (_a >= _b) ? _a - _b : _b - _a;
    }

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

    function _computeCR(uint256 _coll, uint256 _debt, uint256 _price) internal pure returns (uint256) {
        if (_debt > 0) {
            uint256 newCollRatio = (_coll * _price) / _debt;

            return newCollRatio;
        }
        // Return the maximal value for uint256 if the Position has a debt of 0. Represents "infinite" CR.
        else {
            // if (_debt == 0)
            return 2 ** 256 - 1;
        }
    }

    function _computeCR(uint256 _coll, uint256 _debt) internal pure returns (uint256) {
        if (_debt > 0) {
            uint256 newCollRatio = (_coll) / _debt;

            return newCollRatio;
        }
        // Return the maximal value for uint256 if the Position has a debt of 0. Represents "infinite" CR.
        else {
            // if (_debt == 0)
            return 2 ** 256 - 1;
        }
    }

    function _isApproxEqAbs(uint256 a, uint256 b, uint256 tolerance) internal pure returns (bool) {
        return a > b ? (a - b) <= tolerance : (b - a) <= tolerance;
    }

    function _isWithinToleranceAbove(
        uint256 a,
        uint256 b,
        uint256 tolerance
    ) internal pure returns (bool) {
        if (a < b) return false;
        return (a - b) <= tolerance;
    }

    function _isWithinToleranceBelow(
        uint256 a,
        uint256 b,
        uint256 tolerance
    ) internal pure returns (bool) {
        if (a > b) return false;
        return (b - a) <= tolerance;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

import "../interfaces/core/ICore.sol";

/**
    @title Prop Ownable
    @notice Contracts inheriting `PropOwnable` have the same owner as `PropCore`.
            The ownership cannot be independently modified or renounced.
    @dev In the contracts that use CORE to interact with protocol instance specific parameters,
            the immutable will be instanced with PropCore.sol, eitherway, it will be MetaCore.sol
 */
contract PropOwnable {
    ICore public immutable CORE;

    constructor(address _core) {
        CORE = ICore(_core);
    }

    modifier onlyOwner() {
        require(msg.sender == CORE.owner(), "Only owner");
        _;
    }

    function owner() public view returns (address) {
        return CORE.owner();
    }

    function guardian() public view returns (address) {
        return CORE.guardian();
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` 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:
     *
     * - `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 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current 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.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

import {IMetaCore} from "src/interfaces/core/IMetaCore.sol";

interface ICore {

    // --- Public variables ---
    function metaCore() external view returns (IMetaCore);
    function startTime() external view returns (uint256);
    function CCR() external view returns (uint256);
    function dmBootstrapPeriod() external view returns (uint64);
    function isPeriphery(address peripheryContract) external view returns (bool);

    // --- External functions ---

    function setPeripheryEnabled(address _periphery, bool _enabled) external;
    function setPMBootstrapPeriod(address dm, uint64 _bootstrapPeriod) external;
    function setNewCCR(uint256 _CCR) external;

    function priceFeed() external view returns (address);
    function owner() external view returns (address);
    function pendingOwner() external view returns (address);
    function guardian() external view returns (address);
    function feeReceiver() external view returns (address);
    function paused() external view returns (bool);
    function lspBootstrapPeriod() external view returns (uint64);
    function getLspEntryFee(address rebalancer) external view returns (uint16);
    function getLspExitFee(address rebalancer) external view returns (uint16);
    function interestProtocolShare() external view returns (uint16);
    function defaultInterestReceiver() external view returns (address);

    // --- Events ---
    event CCRSet(uint256 initialCCR);
    event PMBootstrapPeriodSet(address dm, uint64 bootstrapPeriod);
    event PeripheryEnabled(address indexed periphery, bool enabled);
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

interface IMetaCore {
    // ---------------------------------
    // Structures
    // ---------------------------------
    struct FeeInfo {
        bool existsForDebtToken;
        uint16 debtTokenFee;
    }

    struct RebalancerFeeInfo {
        bool exists;
        uint16 entryFee;
        uint16 exitFee;
    }

    // ---------------------------------
    // Public constants
    // ---------------------------------
    function OWNERSHIP_TRANSFER_DELAY() external view returns (uint256);
    function DEFAULT_FLASH_LOAN_FEE() external view returns (uint16);

    // ---------------------------------
    // Public state variables
    // ---------------------------------
    function debtToken() external view returns (address);
    function lspEntryFee() external view returns (uint16);
    function lspExitFee() external view returns (uint16);
    function interestProtocolShare() external view returns (uint16);
    /// @dev Default interest receiver for all PositionManagers, unless overriden in the respective PM
    function defaultInterestReceiver() external view returns (address);

    function feeReceiver() external view returns (address);
    function priceFeed() external view returns (address);
    function owner() external view returns (address);
    function pendingOwner() external view returns (address);
    function ownershipTransferDeadline() external view returns (uint256);
    function guardian() external view returns (address);
    function paused() external view returns (bool);
    function lspBootstrapPeriod() external view returns (uint64);

    // ---------------------------------
    // External functions
    // ---------------------------------
    function setFeeReceiver(address _feeReceiver) external;
    function setPriceFeed(address _priceFeed) external;
    function setGuardian(address _guardian) external;

    /**
     * @notice Global pause/unpause
     *         Pausing halts new deposits/borrowing across the protocol
     */
    function setPaused(bool _paused) external;

    /**
     * @notice Extend or change the LSP bootstrap period,
     *         after which certain protocol mechanics change
     */
    function setLspBootstrapPeriod(uint64 _bootstrapPeriod) external;

    /**
     * @notice Set a custom flash-loan fee for a given periphery contract
     * @param _periphery Target contract that will get this custom fee
     * @param _debtTokenFee Fee in basis points (bp)
     * @param _existsForDebtToken Whether this custom fee is used when the caller = `debtToken`
     */
    function setPeripheryFlashLoanFee(address _periphery, uint16 _debtTokenFee, bool _existsForDebtToken) external;

    /**
     * @notice Begin the ownership transfer process
     * @param newOwner The address proposed to be the new owner
     */
    function commitTransferOwnership(address newOwner) external;

    /**
     * @notice Finish the ownership transfer, after the mandatory delay
     */
    function acceptTransferOwnership() external;

    /**
     * @notice Revoke a pending ownership transfer
     */
    function revokeTransferOwnership() external;

    /**
     * @notice Look up a custom flash-loan fee for a specific periphery contract
     * @param peripheryContract The contract that might have a custom fee
     * @return The flash-loan fee in basis points
     */
    function getPeripheryFlashLoanFee(address peripheryContract) external view returns (uint16);

    /**
     * @notice Set / override entry & exit fees for a special rebalancer contract
     */
    function setRebalancerFee(address _rebalancer, uint16 _entryFee, uint16 _exitFee) external;

    /**
     * @notice Set the LSP entry fee globally
     * @param _fee Fee in basis points
     */
    function setEntryFee(uint16 _fee) external;

    /**
     * @notice Set the LSP exit fee globally
     * @param _fee Fee in basis points
     */
    function setExitFee(uint16 _fee) external;

    /**
     * @notice Set the interest protocol share globally to all PositionManagers
     * @param _interestProtocolShare Share in basis points
     */
    function setInterestProtocolShare(uint16 _interestProtocolShare) external;

    /**
     * @notice Look up the LSP entry fee for a rebalancer
     * @param rebalancer Possibly has a special fee
     * @return The entry fee in basis points
     */
    function getLspEntryFee(address rebalancer) external view returns (uint16);

    /**
     * @notice Look up the LSP exit fee for a rebalancer
     * @param rebalancer Possibly has a special fee
     * @return The exit fee in basis points
     */
    function getLspExitFee(address rebalancer) external view returns (uint16);

    // ---------------------------------
    // Events
    // ---------------------------------
    event NewOwnerCommitted(address indexed owner, address indexed pendingOwner, uint256 deadline);
    event NewOwnerAccepted(address indexed oldOwner, address indexed newOwner);
    event NewOwnerRevoked(address indexed owner, address indexed revokedOwner);

    event FeeReceiverSet(address indexed feeReceiver);
    event PriceFeedSet(address indexed priceFeed);
    event GuardianSet(address indexed guardian);
    event PeripheryFlashLoanFee(address indexed periphery, uint16 debtTokenFee);
    event LSPBootstrapPeriodSet(uint64 bootstrapPeriod);
    event RebalancerFees(address indexed rebalancer, uint16 entryFee, uint16 exitFee);
    event EntryFeeSet(uint16 fee);
    event ExitFeeSet(uint16 fee);
    event InterestProtocolShareSet(uint16 interestProtocolShare);
    event DefaultInterestReceiverSet(address indexed defaultInterestReceiver);
    event Paused();
    event Unpaused();
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin-upgradeable/contracts/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "solady/=lib/solady/src/",
    "@solmate/=lib/solmate/src/",
    "@chimera/=lib/chimera/src/",
    "forge-std/=lib/forge-std/src/",
    "@uniswap/v3-core/=lib/v3-core/",
    "@uniswap/v3-periphery/=lib/v3-periphery/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "chimera/=lib/chimera/src/",
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "rewards/=lib/rewards/",
    "solmate/=lib/solmate/src/",
    "uniswap/=lib/uniswap/",
    "v3-core/=lib/v3-core/contracts/",
    "v3-periphery/=lib/v3-periphery/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_core","type":"address"},{"internalType":"address","name":"_gasPoolAddress","type":"address"},{"internalType":"address","name":"_debtTokenAddress","type":"address"},{"internalType":"address","name":"_borrowerOperations","type":"address"},{"internalType":"address","name":"_liquidationManager","type":"address"},{"internalType":"uint256","name":"_gasCompensation","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_baseRate","type":"uint256"}],"name":"BaseRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"CollateralSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_interestReceiver","type":"address"}],"name":"InterestReceiverSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_L_collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_L_debt","type":"uint256"}],"name":"LTermsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_lastFeeOpTime","type":"uint256"}],"name":"LastFeeOpTimeUpdated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"minuteDecayFactor","type":"uint256"},{"internalType":"uint256","name":"redemptionFeeFloor","type":"uint256"},{"internalType":"uint256","name":"maxRedemptionFee","type":"uint256"},{"internalType":"uint256","name":"borrowingFeeFloor","type":"uint256"},{"internalType":"uint256","name":"maxBorrowingFee","type":"uint256"},{"internalType":"uint256","name":"interestRateInBps","type":"uint256"},{"internalType":"uint256","name":"maxDebt","type":"uint256"},{"internalType":"uint256","name":"MCR","type":"uint256"},{"internalType":"address","name":"collVaultRouter","type":"address"}],"indexed":false,"internalType":"struct IFactory.DeploymentParams","name":"params","type":"tuple"}],"name":"NewParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_newIndex","type":"uint256"}],"name":"PositionIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_L_collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_L_debt","type":"uint256"}],"name":"PositionSnapshotsUpdated","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 PositionManager.PositionManagerOperation","name":"_operation","type":"uint8"}],"name":"PositionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_priceFeed","type":"address"}],"name":"PriceFeedUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"_attemptedDebtAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_actualDebtAmount","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":"uint256","name":"_totalStakesSnapshot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_totalCollateralSnapshot","type":"uint256"}],"name":"SystemSnapshotsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_newTotalStakes","type":"uint256"}],"name":"TotalStakesUpdated","type":"event"},{"inputs":[],"name":"CORE","outputs":[{"internalType":"contract ICore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEBT_GAS_COMPENSATION","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_debt","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":"PERCENT_DIVISOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"Positions","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"enum PositionManager.Status","name":"status","type":"uint8"},{"internalType":"uint128","name":"arrayIndex","type":"uint128"},{"internalType":"uint256","name":"activeInterestIndex","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUNSETTING_INTEREST_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeInterestIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"collSurplus","type":"uint256"}],"name":"addCollateralSurplus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"applyPendingRewards","outputs":[{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerOperations","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowingFeeFloor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"claimCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"collAmount","type":"uint256"},{"internalType":"uint256","name":"debtAmount","type":"uint256"}],"name":"closePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"closePositionByLiquidation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collVaultRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectInterests","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"debtToken","outputs":[{"internalType":"contract IDebtToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debt","type":"uint256"}],"name":"decayBaseRateAndGetBorrowingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"coll","type":"uint256"}],"name":"decreaseDebtAndSendCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultedCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultedDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fetchPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_liquidator","type":"address"},{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"uint256","name":"_coll","type":"uint256"},{"internalType":"uint256","name":"_collSurplus","type":"uint256"},{"internalType":"uint256","name":"_debtGasComp","type":"uint256"},{"internalType":"uint256","name":"_collGasComp","type":"uint256"}],"name":"finalizeLiquidation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debt","type":"uint256"}],"name":"getBorrowingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debt","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":"pendingDebtReward","type":"uint256"},{"internalType":"uint256","name":"pendingCollateralReward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemColl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntireSystemDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getNominalICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPendingCollAndDebtRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPositionCollAndDebt","outputs":[{"internalType":"uint256","name":"coll","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getPositionFromPositionOwnersArray","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPositionOwnersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPositionStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"getPositionStatus","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":[],"name":"getTotalActiveCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalActiveDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWeek","outputs":[{"internalType":"uint256","name":"week","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"hasPendingRewards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestPayable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastActiveIndexUpdate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastCollateralError_Redistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastDebtError_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":"liquidationManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxBorrowingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRedemptionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSystemDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minuteDecayFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"uint256","name":"_collateral","type":"uint256"}],"name":"movePendingPositionRewardsToActiveBalances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"uint256","name":"_compositeDebt","type":"uint256"},{"internalType":"uint256","name":"NICR","type":"uint256"},{"internalType":"address","name":"_upperHint","type":"address"},{"internalType":"address","name":"_lowerHint","type":"address"}],"name":"openPosition","outputs":[{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"uint256","name":"arrayIndex","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeed","outputs":[{"internalType":"contract IPriceFeed","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debtAmount","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":[],"name":"redemptionFeeFloor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardSnapshots","outputs":[{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_priceFeedAddress","type":"address"},{"internalType":"address","name":"_sortedPositionsAddress","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collVaultRouter","type":"address"}],"name":"setCollVaultRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_interestReceiver","type":"address"}],"name":"setInterestReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"minuteDecayFactor","type":"uint256"},{"internalType":"uint256","name":"redemptionFeeFloor","type":"uint256"},{"internalType":"uint256","name":"maxRedemptionFee","type":"uint256"},{"internalType":"uint256","name":"borrowingFeeFloor","type":"uint256"},{"internalType":"uint256","name":"maxBorrowingFee","type":"uint256"},{"internalType":"uint256","name":"interestRateInBps","type":"uint256"},{"internalType":"uint256","name":"maxDebt","type":"uint256"},{"internalType":"uint256","name":"MCR","type":"uint256"},{"internalType":"address","name":"collVaultRouter","type":"address"}],"internalType":"struct IFactory.DeploymentParams","name":"params","type":"tuple"}],"name":"setParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_priceFeedAddress","type":"address"}],"name":"setPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sortedPositions","outputs":[{"internalType":"contract ISortedPositions","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startSunset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sunsetting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"surplusBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"systemDeploymentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[],"name":"updateBalances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isDebtIncrease","type":"bool"},{"internalType":"uint256","name":"_debtChange","type":"uint256"},{"internalType":"uint256","name":"_netDebtChange","type":"uint256"},{"internalType":"bool","name":"_isCollIncrease","type":"bool"},{"internalType":"uint256","name":"_collChange","type":"uint256"},{"internalType":"address","name":"_upperHint","type":"address"},{"internalType":"address","name":"_lowerHint","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"updatePositionFromAdjustment","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

610160604052348015610010575f80fd5b50604051614dd9380380614dd983398101604081905261002f9161018c565b60808190526001600160a01b03861660a0819052604080516378e9792560e01b815290518892916378e979259160048083019260209291908290030181865afa15801561007e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100a291906101fa565b60c052506001600160a01b03861615806100c357506001600160a01b038516155b806100d557506001600160a01b038416155b806100e757506001600160a01b038316155b806100f957506001600160a01b038216155b1561014a5760405162461bcd60e51b815260206004820152601a60248201527f506f736974696f6e4d616e616765723a20302061646472657373000000000000604482015260640160405180910390fd5b506001600160a01b039384166101205291831661014052821660e052166101005250610211565b80516001600160a01b0381168114610187575f80fd5b919050565b5f805f805f8060c087890312156101a1575f80fd5b6101aa87610171565b95506101b860208801610171565b94506101c660408801610171565b93506101d460608801610171565b92506101e260808801610171565b91505f60a08801519050809150509295509295509295565b5f6020828403121561020a575f80fd5b5051919050565b60805160a05160c05160e051610100516101205161014051614aac61032d5f395f81816109e20152818161164e0152818161176401528181611fcf015281816124bd01528181612a9d01528181613af501528181613b760152613ffa01525f8181612a66015261402701525f81816104b7015261311c01525f818161069a01528181611e9c01528181612b3601526137d001525f6117dd01525f818161062901528181610cbe01528181610db8015281816110d7015281816111690152818161132401528181611506015281816115b40152818161167d015281816118200152818161187c01528181611db001526123a901525f81816105b201528181613675015281816136f00152818161372f01526140dd0152614aac5ff3fe608060405234801561000f575f80fd5b50600436106103e6575f3560e01c806301c799fc146103ea5780630b076557146104125780630b216022146104335780630fdb11cf146104485780631289fefb1461045e5780631673c79a1461046657806316c38b3c1461048c5780631983fb401461049f5780631ef3a04c146104b25780631f68f20a146104e657806320d17d7b146104ef578063219efc46146104f85780632b11551a1461050b5780632d587e10146105135780632f41d9e11461051c5780632f5c83a81461052f57806331c7a2611461053857806333d53b5914610541578063363bf9641461054a5780633cf1b05b1461055d5780634056d9fc14610565578063452a93201461056e578063477d66cf146105765780634820fe56146105895780634870dd9a146105915780634af797341461059a5780634ba4a28b146105ad5780635728ddda146105d45780635c975abb146105e7578063631203b01461060957806366ca4a211461061c5780636b6c0774146106245780636c302c761461064b5780636f3fe4041461065e578063720fbe2c14610666578063724e78da1461066f578063741bef1a1461068257806377553ad414610695578063789c6b93146106bc578063794e5724146106cf578063795d26c3146106d85780637a83a34e146106e05780637c3a00fd146106e85780637e95d664146106f1578063807d138d146106fa578063874d6d8114610703578063887105d31461070b5780638bbb1de8146107135780638da5cb5b1461071c57806391245f9f146107245780639484fb8e1461072d57806396660b181461073a57806396d711ff146107435780639bf9b9611461074c5780639e994ca81461075e5780639ed7254214610771578063a20baee61461077a578063a27bcdaa14610789578063b0d8e1811461079c578063b2016bd4146107af578063b279906e146107c2578063b2f29d7b146107d5578063b91af97c146107e8578063bc006c381461080b578063bcd3752614610814578063be2c377b14610827578063bf88dd7114610830578063bf9befb114610892578063c045e47d1461089b578063c52861f2146108ae578063c77cf615146108b6578063c98eef62146108c9578063d128080c146108f4578063d12ae841146108fc578063d23ab4611461090f578063d293c71014610922578063d30612e114610935578063d380a37c1461093d578063d38b055814610946578063d5b356351461094f578063e2ac77b014610962578063e47cf0c314610975578063e9186c941461097e578063ea23a27a14610991578063eb7a7f7e146109a4578063ec38a05d146109b7578063f36b2425146109d5578063f8d89898146109dd575b5f80fd5b6103f2610a04565b604080519384526020840192909252908201526060015b60405180910390f35b6104256104203660046142c6565b610a2a565b6040516104099291906142e1565b6104466104413660046142ef565b610a46565b005b610450610ca9565b604051908152602001610409565b610446610db6565b6104256104743660046142c6565b60216020525f90815260409020805460019091015482565b61044661049a366004614316565b610ec9565b6104506104ad3660046142c6565b610f66565b6104d97f000000000000000000000000000000000000000000000000000000000000000081565b6040516104099190614331565b61045060105481565b610450601e5481565b610446610506366004614345565b610f9b565b61045061102e565b610450600d5481565b61044661052a3660046142c6565b61103f565b610450600a5481565b61045060085481565b61045060155481565b610446610558366004614388565b61105e565b602254610450565b61045060095481565b6104d96110d4565b6104506105843660046143d0565b611155565b601954610450565b6104506103e881565b6104466105a83660046142c6565b611167565b6104507f000000000000000000000000000000000000000000000000000000000000000081565b6104466105e23660046143e7565b611238565b600f546105f990610100900460ff1681565b6040519015158152602001610409565b6104506106173660046143d0565b6112dd565b6104506112e9565b6104d97f000000000000000000000000000000000000000000000000000000000000000081565b61044661065936600461441e565b6112fa565b610446611310565b610450600e5481565b61044661067d3660046142c6565b611322565b6001546104d9906001600160a01b031681565b6104d97f000000000000000000000000000000000000000000000000000000000000000081565b6104466106ca36600461443e565b611428565b61045060045481565b61045061145f565b6104466114b1565b610450600b5481565b61045060065481565b61045060135481565b6104506117d3565b61045061180c565b610450600c5481565b6104d961181d565b61045060075481565b600f546105f99060ff1681565b610450601b5481565b61045060145481565b5f546104d9906001600160a01b031681565b61044661076c3660046142c6565b61187a565b61045060185481565b610450670de0b6b3a764000081565b610425610797366004614468565b611973565b6104506107aa3660046142c6565b611bfb565b6002546104d9906001600160a01b031681565b6104d96107d03660046143d0565b611c1f565b6104506107e33660046143d0565b611c4d565b6107fb6107f63660046142c6565b611c7a565b60405161040994939291906144cb565b61045060055481565b6104466108223660046144e6565b611cfe565b610450601d5481565b61088061083e3660046142c6565b601f6020525f90815260409020805460018201546002830154600384015460049094015492939192909160ff8116916101009091046001600160801b03169086565b60405161040996959493929190614563565b61045060125481565b6003546104d9906001600160a01b031681565b610450612557565b601c546104d9906001600160a01b031681565b6104506108d73660046142c6565b6001600160a01b03165f908152601f602052604090206002015490565b610450612568565b61042561090a3660046142c6565b6125ac565b6103f261091d3660046145ae565b6126c1565b61045061093036600461443e565b6128dd565b610450612903565b61045060115481565b61045060175481565b61045061095d3660046143d0565b612937565b6105f96109703660046142c6565b612949565b61045060165481565b61044661098c366004614649565b6129a8565b61044661099f36600461467b565b6129c4565b6104256109b23660046142c6565b612b07565b6104506109c53660046142c6565b602080525f908152604090205481565b610450612b1f565b6104d97f000000000000000000000000000000000000000000000000000000000000000081565b5f805f610a0f61180c565b610a1761145f565b610a1f610ca9565b925092509250909192565b5f80610a34612b2b565b610a3d83612b93565b91509150915091565b600f5460ff1615610a9b5760405162461bcd60e51b815260206004820152601a60248201527910d85b9b9bdd0818da185b99d94818599d195c881cdd5b9cd95d60321b60448201526064015b60405180910390fd5b670de0b6b3a76400008160e001351015610aeb5760405162461bcd60e51b81526020600482015260116024820152704d43522063616e6e6f74203c203130302560781b6044820152606401610a92565b60055415610b2b57610afb61181d565b6001600160a01b0316336001600160a01b031614610b2b5760405162461bcd60e51b8152600401610a92906146c3565b670d8f91d06741510c813510801590610b4d5750670de07829b2adec28813511155b610b55575f80fd5b8060400135816020013511158015610b795750670de0b6b3a7640000816040013511155b610b81575f80fd5b8060800135816060013511158015610ba55750670de0b6b3a7640000816080013511155b610bad575f80fd5b610bb5612cc2565b508035600555602081013560065560408101356007556060810135600855608081013560095560c0810135600a55610bf5610120820161010083016142c6565b5f80546001600160a01b0319166001600160a01b0392909216919091178155610c246301e133806127106146fb565b610c3d60a0840135676765c793fa10079d601b1b6146fb565b610c479190614726565b9050600b548114610c6557610c5a612d01565b5042600d55600b8190555b60e08201356004556040517fdba82a425948402262be7f293af6e198241329ec2ff19e69724a0bcdcda31f4d90610c9d908490614739565b60405180910390a15050565b6001545f906001600160a01b031680610d3f577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663741bef1a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d18573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d3c91906147b0565b90505b600254604051635670bcc760e11b81526001600160a01b038381169263ace1798e92610d719290911690600401614331565b602060405180830381865afa158015610d8c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610db091906147cb565b91505090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e12573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e3691906147b0565b6001600160a01b0316336001600160a01b031614610e665760405162461bcd60e51b8152600401610a92906146c3565b600f805460ff19166001179055610e7b612d01565b50610e8c6301e133806127106146fb565b610ea3676765c793fa10079d601b1b6113886146fb565b610ead9190614726565b600b5542600d555f6006819055600a8190556010819055600755565b808015610eee5750610ed96110d4565b6001600160a01b0316336001600160a01b0316145b80610f115750610efc61181d565b6001600160a01b0316336001600160a01b0316145b610f4c5760405162461bcd60e51b815260206004820152600c60248201526b155b985d5d1a1bdc9a5e995960a21b6044820152606401610a92565b600f80549115156101000261ff0019909216919091179055565b6001600160a01b0381165f908152601f602052604081206003015460ff166004811115610f9557610f9561454f565b92915050565b610fa3612b2b565b60016001600160a01b0385165f908152601f602052604090206003015460ff166004811115610fd457610fd461454f565b14610ff15760405162461bcd60e51b8152600401610a92906147e2565b610ffa84612d62565b611005846002612dab565b80601a546110139190614823565b601a55611020838361303b565b6110286130a3565b50505050565b5f61103a6010546130f7565b905090565b611047613111565b61105081612d62565b61105b816003612dab565b50565b6003546001600160a01b031615611073575f80fd5b600180546001600160a01b039485166001600160a01b03199182161790915560038054938516938216939093179092556002805491909316911617905542600e819055600f805460ff19169055676765c793fa10079d601b1b600c55600d55565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663452a93206040518163ffffffff1660e01b8152600401602060405180830381865afa158015611131573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061103a91906147b0565b5f610f956111616112e9565b83613183565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111e791906147b0565b6001600160a01b0316336001600160a01b0316146112175760405162461bcd60e51b8152600401610a92906146c3565b5f80546001600160a01b0319166001600160a01b0392909216919091179055565b5f546001600160a01b0316331461124d573391505b6001600160a01b0382165f908152602080526040902054806112b15760405162461bcd60e51b815260206004820181905260248201527f4e6f20636f6c6c61746572616c20617661696c61626c6520746f20636c61696d6044820152606401610a92565b6001600160a01b038084165f9081526020805260408120556002546112d8911683836131a1565b505050565b5f610f95611161612b1f565b5f61103a6112f56131f7565b613233565b611302613111565b61130c828261324d565b5050565b611318613111565b6113206132a6565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561137e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113a291906147b0565b6001600160a01b0316336001600160a01b0316146113d25760405162461bcd60e51b8152600401610a92906146c3565b600180546001600160a01b0319166001600160a01b0383161790556040517fe5b20b8497e4f3e2435ef9c20e2e26b47497ee13745ce1c681ad6640653119e69061141d908390614331565b60405180910390a150565b611430613111565b6001600160a01b0382165f90815260208052604081208054839290611456908490614836565b90915550505050565b601a545f908161146d6132ae565b915050801561149d575f61148d8383676765c793fa10079d601b1b613318565b90506114998184614836565b9250505b601e546114aa9083614836565b9250505090565b6114b9612d01565b50601b54806114ff5760405162461bcd60e51b8152602060048201526012602482015271139bdd1a1a5b99c81d1bc818dbdb1b1958dd60721b6044820152606401610a92565b5f611590827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636d52dffd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611560573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115849190614849565b61ffff16612710613318565b90505f61159d8284614823565b601c549091505f906001600160a01b0316611637577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166371f8424f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561160e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061163291906147b0565b611644565b601c546001600160a01b03165b90508215611747577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166340c10f197f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b3f006746040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116d7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116fb91906147b0565b856040518363ffffffff1660e01b815260040161171992919061486a565b5f604051808303815f87803b158015611730575f80fd5b505af1158015611742573d5f803e3d5ffd5b505050505b81156117c9576040516340c10f1960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906340c10f199061179b908490869060040161486a565b5f604051808303815f87803b1580156117b2575f80fd5b505af11580156117c4573d5f803e3d5ffd5b505050505b50505f601b555050565b5f62093a806118027f000000000000000000000000000000000000000000000000000000000000000042614823565b61103a9190614726565b5f601d5460195461103a9190614836565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611131573d5f803e3d5ffd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118d6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118fa91906147b0565b6001600160a01b0316336001600160a01b03161461192a5760405162461bcd60e51b8152600401610a92906146c3565b601c80546001600160a01b0319166001600160a01b0383169081179091556040517f085af60e444f2bfe4e6ad75d3227d2a8702123a3d663597ed1ca90a5f0c2c89c905f90a250565b600f545f908190610100900460ff161561199f5760405162461bcd60e51b8152600401610a9290614883565b6119a7612b2b565b600f5460ff16156119f95760405162461bcd60e51b815260206004820152601c60248201527b43616e6e6f74206f70656e207768696c652073756e73657474696e6760201b6044820152606401610a92565b6001600160a01b0388165f908152601f602052604090206001600382015460ff166004811115611a2b57611a2b61454f565b03611a785760405162461bcd60e51b815260206004820152601f60248201527f426f72726f7765724f70733a20506f736974696f6e20697320616374697665006044820152606401610a92565b60038101805460ff1916600190811790915581018890558681555f611a9b612d01565b600483018190559050611aad8a6133c0565b611ab682613439565b6003546040516346f7cf8760e01b81529195506001600160a01b0316906346f7cf8790611aed908d908b908b908b906004016148ae565b5f604051808303815f87803b158015611b04575f80fd5b505af1158015611b16573d5f803e3d5ffd5b5050505060228a908060018154018082558091505060019003905f5260205f20015f9091909190916101000a8154816001600160a01b0302191690836001600160a01b031602179055506001602280549050611b729190614823565b600383018054610100600160881b0319166101006001600160801b03841602179055601954909350611ba5908a90614836565b601955601a545f90611bb8908a90614836565b9050600a54601e5482611bcb9190614836565b1115611be95760405162461bcd60e51b8152600401610a92906148d9565b601a5550919890975095505050505050565b5f805f611c0784612b07565b915091505f611c1683836134b6565b95945050505050565b5f60228281548110611c3357611c33614910565b5f918252602090912001546001600160a01b031692915050565b5f611c56612b2b565b5f611c5f612cc2565b9050611c73611c6d82613233565b84613183565b9392505050565b6001600160a01b0381165f908152601f6020526040812080546001820154909290918190611ca7866125ac565b60048301549094509092508015611cdd575f611cc16132ae565b50905081611ccf82896146fb565b611cd99190614726565b9650505b611ce78487614836565b9550611cf38386614836565b945050509193509193565b5f60035f9054906101000a90046001600160a01b03169050611d566040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6006548310158015611d6a57506007548311155b611dae5760405162461bcd60e51b81526020600482015260156024820152744d617820666565206e6f7420696e20626f756e647360581b6044820152606401610a92565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c0c39a6f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e0a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e2e9190614924565b6001600160401b0316600e54611e449190614836565b421015611e865760405162461bcd60e51b815260206004820152601060248201526f1093d3d514d514905417d411549253d160821b6044820152606401610a92565b611e8e610ca9565b8160c00181815250506004547f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b620115d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ef6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f1a91906147cb565b1015611f675760405162461bcd60e51b815260206004820152601c60248201527b21b0b73737ba103932b232b2b6903bb432b7102a21a9101e1026a1a960211b6044820152606401610a92565b5f8911611fb65760405162461bcd60e51b815260206004820181905260248201527f416d6f756e74206d7573742062652067726561746572207468616e207a65726f6044820152606401610a92565b6040516370a0823160e01b815289906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190612004903390600401614331565b602060405180830381865afa15801561201f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061204391906147cb565b10156120885760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610a92565b6120906132a6565b61209861145f565b60e082015288815260c08101515f906120b49084908b906134ea565b156120c05750876121c6565b826001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120fc573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061212091906147b0565b90505b6001600160a01b0381161580159061214f5750670de0b6b3a764000061214d828460c001516128dd565b105b156121c657604051632dc9c0eb60e21b81526001600160a01b0384169063b72703ac90612180908490600401614331565b602060405180830381865afa15801561219b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121bf91906147b0565b9050612123565b845f036121d257606494505b6001600160a01b038116158015906121ea5750815115155b80156121f557505f85115b156122f357846122048161494a565b9550505f836001600160a01b031663b72703ac836040518263ffffffff1660e01b81526004016122349190614331565b602060405180830381865afa15801561224f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061227391906147b0565b905061227e82612b93565b50505f6122978584865f01518760c001518e8e8e61362d565b90508060400151156122aa5750506122f3565b805160208501516122bb9190614836565b60208086019190915281015160408501516122d69190614836565b6040850152805184516122e99190614823565b84525090506121d2565b5f8260400151116123445760405162461bcd60e51b815260206004820152601b60248201527a155b98589b19481d1bc81c995919595b48185b9e48185b5bdd5b9d602a1b6044820152606401610a92565b600f5460ff166123675761236582604001518360c001518460e0015161392e565b505b600f5460ff1661238b5761238661237c61102e565b83604001516139ac565b61238d565b5f5b6060830181905260408301516123a4919086613a1c565b6124317f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b3f006746040518163ffffffff1660e01b8152600401602060405180830381865afa158015612403573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061242791906147b0565b836060015161303b565b816060015182604001516124459190614823565b826080018181525050336001600160a01b03167f08b6f1ce3f9ab2722e8ea40c31a3e3a806a41702c5994f29af43dc0c1f2837df8b84602001518560400151866060015160405161249994939291906144cb565b60405180910390a26020820151604051632770a7eb60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691639dc29fac916124f291339160040161486a565b5f604051808303815f87803b158015612509575f80fd5b505af115801561251b573d5f803e3d5ffd5b5050506020830151601a546125309250614823565b601a55608082015161254390339061303b565b61254b6130a3565b50505050505050505050565b5f61103a6125636131f7565b6130f7565b601a545f90816125766132ae565b91505080156125a6575f6125968383676765c793fa10079d601b1b613318565b90506125a28184614836565b9250505b50919050565b6001600160a01b0381165f9081526021602090815260408083208151808301909252805480835260019091015492820192909252601554839283916125f19190614823565b90505f82602001516016546126069190614823565b90506126128183614836565b158061264d575060016001600160a01b0387165f908152601f602052604090206003015460ff16600481111561264a5761264a61454f565b14155b1561265f57505f958695509350505050565b6001600160a01b0386165f908152601f6020526040902060020154670de0b6b3a764000061268d84836146fb565b6126979190614726565b670de0b6b3a76400006126aa84846146fb565b6126b49190614726565b9550955050505050915091565b5f805f6126cc612b2b565b88806126d557508b5b1561275557600f54610100900460ff16156127025760405162461bcd60e51b8152600401610a9290614883565b600f5460ff16156127555760405162461bcd60e51b815260206004820181905260248201527f43616e6e6f7420696e637265617365207768696c652073756e73657474696e676044820152606401610a92565b6001600160a01b0385165f908152601f602052604090206001600382015460ff1660048111156127875761278761454f565b146127a45760405162461bcd60e51b8152600401610a92906147e2565b80548c156127e8578d156127ce576127bc8c82614836565b90506127c9868d8f613a99565b6127e4565b6127d88c82614823565b90506127e4868e613b5f565b8082555b60018201548a15612839578b1561281b576128038b82614836565b90508a6019546128139190614836565b601955612831565b6128258b82614823565b9050612831878c61303b565b600183018190555b5f61284482846134b6565b905060035f9054906101000a90046001600160a01b03166001600160a01b0316632be212608a838e8e6040518563ffffffff1660e01b815260040161288c94939291906148ae565b5f604051808303815f87803b1580156128a3575f80fd5b505af11580156128b5573d5f803e3d5ffd5b5050505081836128c486613439565b9650965096505050505099509950999650505050505050565b5f805f6128e985612b07565b915091505f6128f9838387613bef565b9695505050505050565b6129136301e133806127106146fb565b61292a676765c793fa10079d601b1b6113886146fb565b6129349190614726565b81565b5f610f95612943612557565b836139ac565b5f60016001600160a01b0383165f908152601f602052604090206003015460ff16600481111561297b5761297b61454f565b1461298757505f919050565b506015546001600160a01b039091165f908152602160205260409020541090565b6129b0613111565b6129ba8383613b5f565b6112d8838261303b565b6129cc613111565b6129d68585613c1d565b60195483156129f1576129e98482614823565b601981905590505b601254601355601d548290612a069083614836565b612a109190614823565b60148190556013546040517f51bf4c63ec3cba9d03d43238abbdd979dd91bd16d9895c74ceea9118c7baaf6092612a4792916142e1565b60405180910390a1604051631062c15f60e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301528881166024830152604482018590527f000000000000000000000000000000000000000000000000000000000000000016906320c582be906064015f604051808303815f87803b158015612ade575f80fd5b505af1158015612af0573d5f803e3d5ffd5b50505050612afe878361303b565b50505050505050565b5f80612b1283611c7a565b5090959194509092505050565b5f61103a601054613233565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146113205760405162461bcd60e51b815260206004820152600d60248201526c43616c6c6572206e6f7420424f60981b6044820152606401610a92565b6001600160a01b0381165f908152601f6020526040812081906001600382015460ff166004811115612bc757612bc761454f565b03612cbc5760048101545f612bda612d01565b835460018501549650945090508381831015612c0f5782612bfb83876146fb565b612c059190614726565b6004850183905594505b6015546001600160a01b0388165f908152602160205260409020541015612cad575f80612c3b896125ac565b9092509050612c4a8289614836565b600187018190559750612c5d8188614836565b9650612c68896133c0565b612c72818361324d565b886001600160a01b03165f80516020614a37833981519152888a89600201545f604051612ca29493929190614973565b60405180910390a250505b848114612cb8578484555b5050505b50915091565b5f80612ccc6131f7565b60108190556040518181529091505f80516020614a578339815191529060200160405180910390a1612cfc613d8c565b919050565b5f805f612d0c6132ae565b909250905080156125a657601a545f612d318284676765c793fa10079d601b1b613318565b9050612d3d8183614836565b601a55601b54612d4e908290614836565b601b555050600c82905542600d5550919050565b6001600160a01b0381165f908152601f6020526040902060020154601254612d8b908290614823565b601255506001600160a01b03165f908152601f6020526040812060020155565b6022546001600160a01b0383165f908152601f6020526040902060038101805484919060ff19166001836004811115612de657612de661454f565b02179055505f6001808301829055818355600483018290556003546001600160a01b038781168452602160205260408420848155830193909355919091169083118015612e9257506001816001600160a01b031663de8fa4316040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e6c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e9091906147cb565b115b15612f9757600382015461010090046001600160801b03165f6022612eb8600187614823565b81548110612ec857612ec8614910565b5f91825260209091200154602280546001600160a01b03909216925082916001600160801b038516908110612eff57612eff614910565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918316808252601f83526040918290206003018054610100600160881b0319166101006001600160801b038816908102919091179091558251918252928101929092527f2391d278b0562640dd6add67aa715b4dab2327f1ec896819ff2a809adea29a36910160405180910390a150505b6022805480612fa857612fa8614995565b5f8281526020902081015f1990810180546001600160a01b0319169055019055604051631484968760e11b81526001600160a01b038216906329092d0e90612ff4908890600401614331565b5f604051808303815f87803b15801561300b575f80fd5b505af115801561301d573d5f803e3d5ffd5b50505060039092018054610100600160881b03191690555050505050565b801561130c578060195461304f9190614823565b6019556040517f342693d2465f6f44931e41128424a0227e0cbc69d1c3917a839e6de71696d44c90613084908490849061486a565b60405180910390a160025461130c906001600160a01b031683836131a1565b6022545f0361132057676765c793fa10079d601b1b600c5542600d555f60128190556013819055601481905560158190556016819055601781905560188190556019819055601a819055601d819055601e55565b5f610f95826006546131099190614836565b600754613dfe565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146113205760405162461bcd60e51b81526020600482015260176024820152762737ba102634b8bab4b230ba34b7b71026b0b730b3b2b960491b6044820152606401610a92565b5f670de0b6b3a764000061319783856146fb565b611c739190614726565b6112d88363a9059cbb60e01b84846040516024016131c092919061486a565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613e13565b5f80613201613ee4565b90505f61321060055483613ef5565b9050670de0b6b3a76400008160105461322991906146fb565b6114aa9190614726565b5f610f95826008546132459190614836565b600954613dfe565b81601e5f82825461325e9190614823565b9250508190555081601a5f8282546132769190614836565b9250508190555080601d5f82825461328e9190614823565b925050819055508060195f8282546114569190614836565b61105b612d01565b600d545f9081904281036132c7575050600c54915f9150565b600b54600c5493508015613312575f6132e08342614823565b90506132ec82826146fb565b93506133048585676765c793fa10079d601b1b613318565b61330e9086614836565b9450505b50509091565b5f80805f19858709858702925082811083820303915050805f0361334f5783828161334557613345614712565b0492505050611c73565b80841161335a575f80fd5b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b60155460165460408051808201825283815260208082018481526001600160a01b0387165f9081526021909252908390209151825551600190910155517fc2910901d6342262f6857ac83c05564d215eef237fb5bcdf69013b47bbf4b8a89061342c90849084906142e1565b60405180910390a1505050565b5f806134488360010154613f9d565b60028401805490829055601254919250905f908390613468908490614823565b6134729190614836565b60128190556040518181529091507f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae5203829060200160405180910390a150909392505050565b5f81156134e257816134d168056bc75e2d63100000856146fb565b6134db9190614726565b9050610f95565b505f19610f95565b5f6001600160a01b03831615806135695750604051630bb7c8fd60e31b81526001600160a01b03851690635dbe47e890613528908690600401614331565b602060405180830381865afa158015613543573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061356791906149a9565b155b806135845750670de0b6b3a764000061358284846128dd565b105b1561359057505f611c73565b60405163765e015960e01b81525f906001600160a01b0386169063765e0159906135be908790600401614331565b602060405180830381865afa1580156135d9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135fd91906147b0565b90506001600160a01b0381161580611c165750670de0b6b3a764000061362382856128dd565b1095945050505050565b61365060405180606001604052805f81526020015f81526020015f151581525090565b6001600160a01b0387165f908152601f60205260409020805461369f90889061369a907f000000000000000000000000000000000000000000000000000000000000000090614823565b613dfe565b80835286906136b790670de0b6b3a7640000906146fb565b6136c19190614726565b6020830152815181545f916136d591614823565b90505f836020015183600101546136ec9190614823565b90507f0000000000000000000000000000000000000000000000000000000000000000820361378e5761371e8a612d62565b6137298a6004612dab565b6137548a7f000000000000000000000000000000000000000000000000000000000000000083613fe3565b896001600160a01b03165f80516020614a378339815191525f805f60036040516137819493929190614973565b60405180910390a261391f565b5f61379982846134b6565b90505f8187116137b2576137ad8783614823565b6137bc565b6137bc8288614823565b90506601c6bf5263400081118061385957507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663969c24526040518163ffffffff1660e01b8152600401602060405180830381865afa15801561382a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061384e91906147cb565b613857856140d7565b105b156138705750506001604085015250613923915050565b5060405163015f109360e51b81526001600160a01b038d1690632be21260906138a3908e9085908d908d906004016148ae565b5f604051808303815f87803b1580156138ba575f80fd5b505af11580156138cc573d5f803e3d5ffd5b50505083855550600184018290556138e384613439565b508a6001600160a01b03165f80516020614a378339815191528484876002015460036040516139159493929190614973565b60405180910390a2505b5050505b979650505050505050565b5f806139386131f7565b90505f8361394686886146fb565b6139509190614726565b90505f61395e600283614726565b6139689084614836565b905061397c81670de0b6b3a7640000613dfe565b60108190556040518181529091505f80516020614a578339815191529060200160405180910390a16128f9613d8c565b5f80670de0b6b3a76400006139c184866146fb565b6139cb9190614726565b9050828110611c735760405162461bcd60e51b815260206004820152601f60248201527f46656520657863656564732072657475726e656420636f6c6c61746572616c006044820152606401610a92565b5f825f03613a2a575f613a47565b82613a3d670de0b6b3a7640000866146fb565b613a479190614726565b9050818111156110285760405162461bcd60e51b815260206004820152601d60248201527f4665652065786365656465642070726f7669646564206d6178696d756d0000006044820152606401610a92565b5f82601a54613aa89190614836565b9050600a54601e5482613abb9190614836565b1115613ad95760405162461bcd60e51b8152600401610a92906148d9565b601a8190556040516340c10f1960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906340c10f1990613b2c908790869060040161486a565b5f604051808303815f87803b158015613b43575f80fd5b505af1158015613b55573d5f803e3d5ffd5b5050505050505050565b604051632770a7eb60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690639dc29fac90613bad908590859060040161486a565b5f604051808303815f87803b158015613bc4575f80fd5b505af1158015613bd6573d5f803e3d5ffd5b5050505080601a54613be89190614823565b601a555050565b5f8215613c15575f83613c0284876146fb565b613c0c9190614726565b9150611c739050565b505f19611c73565b815f03613c28575050565b6017545f90613c3f670de0b6b3a7640000846146fb565b613c499190614836565b90505f601854670de0b6b3a764000085613c6391906146fb565b613c6d9190614836565b6012549091505f613c7e8285614726565b90505f613c8b8385614726565b9050613c9783836146fb565b613ca19086614823565b601755613cae83826146fb565b613cb89085614823565b6018556015545f90613ccb908490614836565b90505f82601654613cdc9190614836565b601583905560168190556040519091507f9f8bc8ab0daf5bceef75ecfd2085d1fcc6548c657ea970d9a23a60610d0737e390613d1b90849084906142e1565b60405180910390a188601a5f828254613d349190614823565b9250508190555088601e5f828254613d4c9190614836565b9250508190555087601d5f828254613d649190614836565b925050819055508760195f828254613d7c9190614823565b9091555050505050505050505050565b5f60115442613d9b9190614823565b9050603c811061105b57603c613daf613ee4565b613db991906146fb565b60115f828254613dc99190614836565b90915550506040514281527f860f8d2f0c74dd487e89e2883e3b25b8159ce1e1b3433a291cba7b82c508f3bc9060200161141d565b5f818310613e0c5781611c73565b5090919050565b5f613e67826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166141029092919063ffffffff16565b8051909150156112d85780806020019051810190613e8591906149a9565b6112d85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a92565b5f603c601154426118029190614823565b5f631f540500821115613f0a57631f54050091505b815f03613f205750670de0b6b3a7640000610f95565b670de0b6b3a764000083835b6001811115613f9357613f406002826149c4565b5f03613f6457613f508283614118565b9150613f5d600282614726565b9050613f2c565b613f6e8284614118565b9250613f7a8283614118565b91506002613f89600183614823565b613f5d9190614726565b6128f98284614118565b6014545f908190808203613fb357839150613fdc565b60135480613fc357613fc36149d7565b81613fce82876146fb565b613fd89190614726565b9250505b5092915050565b604051632770a7eb60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690639dc29fac90614051907f000000000000000000000000000000000000000000000000000000000000000090869060040161486a565b5f604051808303815f87803b158015614068575f80fd5b505af115801561407a573d5f803e3d5ffd5b5050505081601a5461408c9190614823565b601a556001600160a01b0383165f908152602080526040812080548392906140b5908490614836565b925050819055508060195f8282546140cd9190614823565b9091555050505050565b5f610f957f000000000000000000000000000000000000000000000000000000000000000083614823565b606061411084845f8561414e565b949350505050565b5f8061412483856146fb565b9050670de0b6b3a764000061413a600282614726565b6141449083614836565b6141109190614726565b6060824710156141af5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a92565b5f80866001600160a01b031685876040516141ca91906149eb565b5f6040518083038185875af1925050503d805f8114614204576040519150601f19603f3d011682016040523d82523d5f602084013e614209565b606091505b509150915061392387838387606083156142835782515f0361427c576001600160a01b0385163b61427c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a92565b5081614110565b61411083838151156142985781518083602001fd5b8060405162461bcd60e51b8152600401610a929190614a01565b6001600160a01b038116811461105b575f80fd5b5f602082840312156142d6575f80fd5b8135611c73816142b2565b918252602082015260400190565b5f610120828403128015614301575f80fd5b509092915050565b801515811461105b575f80fd5b5f60208284031215614326575f80fd5b8135611c7381614309565b6001600160a01b0391909116815260200190565b5f805f8060808587031215614358575f80fd5b8435614363816142b2565b93506020850135614373816142b2565b93969395505050506040820135916060013590565b5f805f6060848603121561439a575f80fd5b83356143a5816142b2565b925060208401356143b5816142b2565b915060408401356143c5816142b2565b809150509250925092565b5f602082840312156143e0575f80fd5b5035919050565b5f80604083850312156143f8575f80fd5b8235614403816142b2565b91506020830135614413816142b2565b809150509250929050565b5f806040838503121561442f575f80fd5b50508035926020909101359150565b5f806040838503121561444f575f80fd5b823561445a816142b2565b946020939093013593505050565b5f805f805f8060c0878903121561447d575f80fd5b8635614488816142b2565b955060208701359450604087013593506060870135925060808701356144ad816142b2565b915060a08701356144bd816142b2565b809150509295509295509295565b93845260208401929092526040830152606082015260800190565b5f805f805f805f60e0888a0312156144fc575f80fd5b87359650602088013561450e816142b2565b9550604088013561451e816142b2565b9450606088013561452e816142b2565b9699959850939660808101359560a0820135955060c0909101359350915050565b634e487b7160e01b5f52602160045260245ffd5b868152602081018690526040810185905260c08101600585106145885761458861454f565b60608201949094526001600160801b0392909216608083015260a0909101529392505050565b5f805f805f805f805f6101208a8c0312156145c7575f80fd5b89356145d281614309565b985060208a0135975060408a0135965060608a01356145f081614309565b955060808a0135945060a08a0135614607816142b2565b935060c08a0135614617816142b2565b925060e08a0135614627816142b2565b91506101008a0135614638816142b2565b809150509295985092959850929598565b5f805f6060848603121561465b575f80fd5b8335614666816142b2565b95602085013595506040909401359392505050565b5f805f805f8060c08789031215614690575f80fd5b863561469b816142b2565b9860208801359850604088013597606081013597506080810135965060a00135945092505050565b6020808252600a908201526927b7363c9037bbb732b960b11b604082015260600190565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610f9557610f956146e7565b634e487b7160e01b5f52601260045260245ffd5b5f8261473457614734614712565b500490565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c0808301359082015260e080830135908201526101208101610100830135614797816142b2565b6001600160a01b03166101009290920191909152919050565b5f602082840312156147c0575f80fd5b8151611c73816142b2565b5f602082840312156147db575f80fd5b5051919050565b60208082526021908201527f506f736974696f6e20636c6f736564206f7220646f6573206e6f7420657869736040820152601d60fa1b606082015260800190565b81810381811115610f9557610f956146e7565b80820180821115610f9557610f956146e7565b5f60208284031215614859575f80fd5b815161ffff81168114611c73575f80fd5b6001600160a01b03929092168252602082015260400190565b60208082526011908201527010dbdb1b185d195c985b0814185d5cd959607a1b604082015260600190565b6001600160a01b03948516815260208101939093529083166040830152909116606082015260800190565b6020808252601d908201527f436f6c6c61746572616c2064656274206c696d69742072656163686564000000604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215614934575f80fd5b81516001600160401b0381168114611c73575f80fd5b5f81614958576149586146e7565b505f190190565b6004811061496f5761496f61454f565b9052565b848152602081018490526040810183905260808101611c16606083018461495f565b634e487b7160e01b5f52603160045260245ffd5b5f602082840312156149b9575f80fd5b8151611c7381614309565b5f826149d2576149d2614712565b500690565b634e487b7160e01b5f52600160045260245ffd5b5f82518060208501845e5f920191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f8301168401019150509291505056fe4fe7fb62190647b8a7596709832f68a365082b18206e55ae330b95593c369affc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600ca264697066735822122014b0870a961a31054827d231caff25708082a1a8f53366c14431671fb561448e64736f6c634300081a0033000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d57000000000000000000000000130541a39cfaa89c9ed8f387d479b277321d1ad40000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd4000000000000000000000000fa2de0dcaa6dfdc0d01ad2677258a641bdae08400000000000000000000000000000000000000000000000000de0b6b3a7640000

Deployed Bytecode

0x608060405234801561000f575f80fd5b50600436106103e6575f3560e01c806301c799fc146103ea5780630b076557146104125780630b216022146104335780630fdb11cf146104485780631289fefb1461045e5780631673c79a1461046657806316c38b3c1461048c5780631983fb401461049f5780631ef3a04c146104b25780631f68f20a146104e657806320d17d7b146104ef578063219efc46146104f85780632b11551a1461050b5780632d587e10146105135780632f41d9e11461051c5780632f5c83a81461052f57806331c7a2611461053857806333d53b5914610541578063363bf9641461054a5780633cf1b05b1461055d5780634056d9fc14610565578063452a93201461056e578063477d66cf146105765780634820fe56146105895780634870dd9a146105915780634af797341461059a5780634ba4a28b146105ad5780635728ddda146105d45780635c975abb146105e7578063631203b01461060957806366ca4a211461061c5780636b6c0774146106245780636c302c761461064b5780636f3fe4041461065e578063720fbe2c14610666578063724e78da1461066f578063741bef1a1461068257806377553ad414610695578063789c6b93146106bc578063794e5724146106cf578063795d26c3146106d85780637a83a34e146106e05780637c3a00fd146106e85780637e95d664146106f1578063807d138d146106fa578063874d6d8114610703578063887105d31461070b5780638bbb1de8146107135780638da5cb5b1461071c57806391245f9f146107245780639484fb8e1461072d57806396660b181461073a57806396d711ff146107435780639bf9b9611461074c5780639e994ca81461075e5780639ed7254214610771578063a20baee61461077a578063a27bcdaa14610789578063b0d8e1811461079c578063b2016bd4146107af578063b279906e146107c2578063b2f29d7b146107d5578063b91af97c146107e8578063bc006c381461080b578063bcd3752614610814578063be2c377b14610827578063bf88dd7114610830578063bf9befb114610892578063c045e47d1461089b578063c52861f2146108ae578063c77cf615146108b6578063c98eef62146108c9578063d128080c146108f4578063d12ae841146108fc578063d23ab4611461090f578063d293c71014610922578063d30612e114610935578063d380a37c1461093d578063d38b055814610946578063d5b356351461094f578063e2ac77b014610962578063e47cf0c314610975578063e9186c941461097e578063ea23a27a14610991578063eb7a7f7e146109a4578063ec38a05d146109b7578063f36b2425146109d5578063f8d89898146109dd575b5f80fd5b6103f2610a04565b604080519384526020840192909252908201526060015b60405180910390f35b6104256104203660046142c6565b610a2a565b6040516104099291906142e1565b6104466104413660046142ef565b610a46565b005b610450610ca9565b604051908152602001610409565b610446610db6565b6104256104743660046142c6565b60216020525f90815260409020805460019091015482565b61044661049a366004614316565b610ec9565b6104506104ad3660046142c6565b610f66565b6104d97f000000000000000000000000fa2de0dcaa6dfdc0d01ad2677258a641bdae084081565b6040516104099190614331565b61045060105481565b610450601e5481565b610446610506366004614345565b610f9b565b61045061102e565b610450600d5481565b61044661052a3660046142c6565b61103f565b610450600a5481565b61045060085481565b61045060155481565b610446610558366004614388565b61105e565b602254610450565b61045060095481565b6104d96110d4565b6104506105843660046143d0565b611155565b601954610450565b6104506103e881565b6104466105a83660046142c6565b611167565b6104507f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b6104466105e23660046143e7565b611238565b600f546105f990610100900460ff1681565b6040519015158152602001610409565b6104506106173660046143d0565b6112dd565b6104506112e9565b6104d97f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d5781565b61044661065936600461441e565b6112fa565b610446611310565b610450600e5481565b61044661067d3660046142c6565b611322565b6001546104d9906001600160a01b031681565b6104d97f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd481565b6104466106ca36600461443e565b611428565b61045060045481565b61045061145f565b6104466114b1565b610450600b5481565b61045060065481565b61045060135481565b6104506117d3565b61045061180c565b610450600c5481565b6104d961181d565b61045060075481565b600f546105f99060ff1681565b610450601b5481565b61045060145481565b5f546104d9906001600160a01b031681565b61044661076c3660046142c6565b61187a565b61045060185481565b610450670de0b6b3a764000081565b610425610797366004614468565b611973565b6104506107aa3660046142c6565b611bfb565b6002546104d9906001600160a01b031681565b6104d96107d03660046143d0565b611c1f565b6104506107e33660046143d0565b611c4d565b6107fb6107f63660046142c6565b611c7a565b60405161040994939291906144cb565b61045060055481565b6104466108223660046144e6565b611cfe565b610450601d5481565b61088061083e3660046142c6565b601f6020525f90815260409020805460018201546002830154600384015460049094015492939192909160ff8116916101009091046001600160801b03169086565b60405161040996959493929190614563565b61045060125481565b6003546104d9906001600160a01b031681565b610450612557565b601c546104d9906001600160a01b031681565b6104506108d73660046142c6565b6001600160a01b03165f908152601f602052604090206002015490565b610450612568565b61042561090a3660046142c6565b6125ac565b6103f261091d3660046145ae565b6126c1565b61045061093036600461443e565b6128dd565b610450612903565b61045060115481565b61045060175481565b61045061095d3660046143d0565b612937565b6105f96109703660046142c6565b612949565b61045060165481565b61044661098c366004614649565b6129a8565b61044661099f36600461467b565b6129c4565b6104256109b23660046142c6565b612b07565b6104506109c53660046142c6565b602080525f908152604090205481565b610450612b1f565b6104d97f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f81565b5f805f610a0f61180c565b610a1761145f565b610a1f610ca9565b925092509250909192565b5f80610a34612b2b565b610a3d83612b93565b91509150915091565b600f5460ff1615610a9b5760405162461bcd60e51b815260206004820152601a60248201527910d85b9b9bdd0818da185b99d94818599d195c881cdd5b9cd95d60321b60448201526064015b60405180910390fd5b670de0b6b3a76400008160e001351015610aeb5760405162461bcd60e51b81526020600482015260116024820152704d43522063616e6e6f74203c203130302560781b6044820152606401610a92565b60055415610b2b57610afb61181d565b6001600160a01b0316336001600160a01b031614610b2b5760405162461bcd60e51b8152600401610a92906146c3565b670d8f91d06741510c813510801590610b4d5750670de07829b2adec28813511155b610b55575f80fd5b8060400135816020013511158015610b795750670de0b6b3a7640000816040013511155b610b81575f80fd5b8060800135816060013511158015610ba55750670de0b6b3a7640000816080013511155b610bad575f80fd5b610bb5612cc2565b508035600555602081013560065560408101356007556060810135600855608081013560095560c0810135600a55610bf5610120820161010083016142c6565b5f80546001600160a01b0319166001600160a01b0392909216919091178155610c246301e133806127106146fb565b610c3d60a0840135676765c793fa10079d601b1b6146fb565b610c479190614726565b9050600b548114610c6557610c5a612d01565b5042600d55600b8190555b60e08201356004556040517fdba82a425948402262be7f293af6e198241329ec2ff19e69724a0bcdcda31f4d90610c9d908490614739565b60405180910390a15050565b6001545f906001600160a01b031680610d3f577f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b031663741bef1a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d18573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d3c91906147b0565b90505b600254604051635670bcc760e11b81526001600160a01b038381169263ace1798e92610d719290911690600401614331565b602060405180830381865afa158015610d8c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610db091906147cb565b91505090565b7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e12573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e3691906147b0565b6001600160a01b0316336001600160a01b031614610e665760405162461bcd60e51b8152600401610a92906146c3565b600f805460ff19166001179055610e7b612d01565b50610e8c6301e133806127106146fb565b610ea3676765c793fa10079d601b1b6113886146fb565b610ead9190614726565b600b5542600d555f6006819055600a8190556010819055600755565b808015610eee5750610ed96110d4565b6001600160a01b0316336001600160a01b0316145b80610f115750610efc61181d565b6001600160a01b0316336001600160a01b0316145b610f4c5760405162461bcd60e51b815260206004820152600c60248201526b155b985d5d1a1bdc9a5e995960a21b6044820152606401610a92565b600f80549115156101000261ff0019909216919091179055565b6001600160a01b0381165f908152601f602052604081206003015460ff166004811115610f9557610f9561454f565b92915050565b610fa3612b2b565b60016001600160a01b0385165f908152601f602052604090206003015460ff166004811115610fd457610fd461454f565b14610ff15760405162461bcd60e51b8152600401610a92906147e2565b610ffa84612d62565b611005846002612dab565b80601a546110139190614823565b601a55611020838361303b565b6110286130a3565b50505050565b5f61103a6010546130f7565b905090565b611047613111565b61105081612d62565b61105b816003612dab565b50565b6003546001600160a01b031615611073575f80fd5b600180546001600160a01b039485166001600160a01b03199182161790915560038054938516938216939093179092556002805491909316911617905542600e819055600f805460ff19169055676765c793fa10079d601b1b600c55600d55565b5f7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b031663452a93206040518163ffffffff1660e01b8152600401602060405180830381865afa158015611131573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061103a91906147b0565b5f610f956111616112e9565b83613183565b7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111e791906147b0565b6001600160a01b0316336001600160a01b0316146112175760405162461bcd60e51b8152600401610a92906146c3565b5f80546001600160a01b0319166001600160a01b0392909216919091179055565b5f546001600160a01b0316331461124d573391505b6001600160a01b0382165f908152602080526040902054806112b15760405162461bcd60e51b815260206004820181905260248201527f4e6f20636f6c6c61746572616c20617661696c61626c6520746f20636c61696d6044820152606401610a92565b6001600160a01b038084165f9081526020805260408120556002546112d8911683836131a1565b505050565b5f610f95611161612b1f565b5f61103a6112f56131f7565b613233565b611302613111565b61130c828261324d565b5050565b611318613111565b6113206132a6565b565b7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561137e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113a291906147b0565b6001600160a01b0316336001600160a01b0316146113d25760405162461bcd60e51b8152600401610a92906146c3565b600180546001600160a01b0319166001600160a01b0383161790556040517fe5b20b8497e4f3e2435ef9c20e2e26b47497ee13745ce1c681ad6640653119e69061141d908390614331565b60405180910390a150565b611430613111565b6001600160a01b0382165f90815260208052604081208054839290611456908490614836565b90915550505050565b601a545f908161146d6132ae565b915050801561149d575f61148d8383676765c793fa10079d601b1b613318565b90506114998184614836565b9250505b601e546114aa9083614836565b9250505090565b6114b9612d01565b50601b54806114ff5760405162461bcd60e51b8152602060048201526012602482015271139bdd1a1a5b99c81d1bc818dbdb1b1958dd60721b6044820152606401610a92565b5f611590827f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316636d52dffd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611560573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115849190614849565b61ffff16612710613318565b90505f61159d8284614823565b601c549091505f906001600160a01b0316611637577f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b03166371f8424f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561160e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061163291906147b0565b611644565b601c546001600160a01b03165b90508215611747577f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f6001600160a01b03166340c10f197f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b031663b3f006746040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116d7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116fb91906147b0565b856040518363ffffffff1660e01b815260040161171992919061486a565b5f604051808303815f87803b158015611730575f80fd5b505af1158015611742573d5f803e3d5ffd5b505050505b81156117c9576040516340c10f1960e01b81526001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f16906340c10f199061179b908490869060040161486a565b5f604051808303815f87803b1580156117b2575f80fd5b505af11580156117c4573d5f803e3d5ffd5b505050505b50505f601b555050565b5f62093a806118027f0000000000000000000000000000000000000000000000000000000069151f8042614823565b61103a9190614726565b5f601d5460195461103a9190614836565b5f7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611131573d5f803e3d5ffd5b7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118d6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118fa91906147b0565b6001600160a01b0316336001600160a01b03161461192a5760405162461bcd60e51b8152600401610a92906146c3565b601c80546001600160a01b0319166001600160a01b0383169081179091556040517f085af60e444f2bfe4e6ad75d3227d2a8702123a3d663597ed1ca90a5f0c2c89c905f90a250565b600f545f908190610100900460ff161561199f5760405162461bcd60e51b8152600401610a9290614883565b6119a7612b2b565b600f5460ff16156119f95760405162461bcd60e51b815260206004820152601c60248201527b43616e6e6f74206f70656e207768696c652073756e73657474696e6760201b6044820152606401610a92565b6001600160a01b0388165f908152601f602052604090206001600382015460ff166004811115611a2b57611a2b61454f565b03611a785760405162461bcd60e51b815260206004820152601f60248201527f426f72726f7765724f70733a20506f736974696f6e20697320616374697665006044820152606401610a92565b60038101805460ff1916600190811790915581018890558681555f611a9b612d01565b600483018190559050611aad8a6133c0565b611ab682613439565b6003546040516346f7cf8760e01b81529195506001600160a01b0316906346f7cf8790611aed908d908b908b908b906004016148ae565b5f604051808303815f87803b158015611b04575f80fd5b505af1158015611b16573d5f803e3d5ffd5b5050505060228a908060018154018082558091505060019003905f5260205f20015f9091909190916101000a8154816001600160a01b0302191690836001600160a01b031602179055506001602280549050611b729190614823565b600383018054610100600160881b0319166101006001600160801b03841602179055601954909350611ba5908a90614836565b601955601a545f90611bb8908a90614836565b9050600a54601e5482611bcb9190614836565b1115611be95760405162461bcd60e51b8152600401610a92906148d9565b601a5550919890975095505050505050565b5f805f611c0784612b07565b915091505f611c1683836134b6565b95945050505050565b5f60228281548110611c3357611c33614910565b5f918252602090912001546001600160a01b031692915050565b5f611c56612b2b565b5f611c5f612cc2565b9050611c73611c6d82613233565b84613183565b9392505050565b6001600160a01b0381165f908152601f6020526040812080546001820154909290918190611ca7866125ac565b60048301549094509092508015611cdd575f611cc16132ae565b50905081611ccf82896146fb565b611cd99190614726565b9650505b611ce78487614836565b9550611cf38386614836565b945050509193509193565b5f60035f9054906101000a90046001600160a01b03169050611d566040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6006548310158015611d6a57506007548311155b611dae5760405162461bcd60e51b81526020600482015260156024820152744d617820666565206e6f7420696e20626f756e647360581b6044820152606401610a92565b7f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b031663c0c39a6f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e0a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e2e9190614924565b6001600160401b0316600e54611e449190614836565b421015611e865760405162461bcd60e51b815260206004820152601060248201526f1093d3d514d514905417d411549253d160821b6044820152606401610a92565b611e8e610ca9565b8160c00181815250506004547f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd46001600160a01b031663b620115d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ef6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f1a91906147cb565b1015611f675760405162461bcd60e51b815260206004820152601c60248201527b21b0b73737ba103932b232b2b6903bb432b7102a21a9101e1026a1a960211b6044820152606401610a92565b5f8911611fb65760405162461bcd60e51b815260206004820181905260248201527f416d6f756e74206d7573742062652067726561746572207468616e207a65726f6044820152606401610a92565b6040516370a0823160e01b815289906001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f16906370a0823190612004903390600401614331565b602060405180830381865afa15801561201f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061204391906147cb565b10156120885760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610a92565b6120906132a6565b61209861145f565b60e082015288815260c08101515f906120b49084908b906134ea565b156120c05750876121c6565b826001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120fc573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061212091906147b0565b90505b6001600160a01b0381161580159061214f5750670de0b6b3a764000061214d828460c001516128dd565b105b156121c657604051632dc9c0eb60e21b81526001600160a01b0384169063b72703ac90612180908490600401614331565b602060405180830381865afa15801561219b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121bf91906147b0565b9050612123565b845f036121d257606494505b6001600160a01b038116158015906121ea5750815115155b80156121f557505f85115b156122f357846122048161494a565b9550505f836001600160a01b031663b72703ac836040518263ffffffff1660e01b81526004016122349190614331565b602060405180830381865afa15801561224f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061227391906147b0565b905061227e82612b93565b50505f6122978584865f01518760c001518e8e8e61362d565b90508060400151156122aa5750506122f3565b805160208501516122bb9190614836565b60208086019190915281015160408501516122d69190614836565b6040850152805184516122e99190614823565b84525090506121d2565b5f8260400151116123445760405162461bcd60e51b815260206004820152601b60248201527a155b98589b19481d1bc81c995919595b48185b9e48185b5bdd5b9d602a1b6044820152606401610a92565b600f5460ff166123675761236582604001518360c001518460e0015161392e565b505b600f5460ff1661238b5761238661237c61102e565b83604001516139ac565b61238d565b5f5b6060830181905260408301516123a4919086613a1c565b6124317f000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d576001600160a01b031663b3f006746040518163ffffffff1660e01b8152600401602060405180830381865afa158015612403573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061242791906147b0565b836060015161303b565b816060015182604001516124459190614823565b826080018181525050336001600160a01b03167f08b6f1ce3f9ab2722e8ea40c31a3e3a806a41702c5994f29af43dc0c1f2837df8b84602001518560400151866060015160405161249994939291906144cb565b60405180910390a26020820151604051632770a7eb60e21b81526001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f1691639dc29fac916124f291339160040161486a565b5f604051808303815f87803b158015612509575f80fd5b505af115801561251b573d5f803e3d5ffd5b5050506020830151601a546125309250614823565b601a55608082015161254390339061303b565b61254b6130a3565b50505050505050505050565b5f61103a6125636131f7565b6130f7565b601a545f90816125766132ae565b91505080156125a6575f6125968383676765c793fa10079d601b1b613318565b90506125a28184614836565b9250505b50919050565b6001600160a01b0381165f9081526021602090815260408083208151808301909252805480835260019091015492820192909252601554839283916125f19190614823565b90505f82602001516016546126069190614823565b90506126128183614836565b158061264d575060016001600160a01b0387165f908152601f602052604090206003015460ff16600481111561264a5761264a61454f565b14155b1561265f57505f958695509350505050565b6001600160a01b0386165f908152601f6020526040902060020154670de0b6b3a764000061268d84836146fb565b6126979190614726565b670de0b6b3a76400006126aa84846146fb565b6126b49190614726565b9550955050505050915091565b5f805f6126cc612b2b565b88806126d557508b5b1561275557600f54610100900460ff16156127025760405162461bcd60e51b8152600401610a9290614883565b600f5460ff16156127555760405162461bcd60e51b815260206004820181905260248201527f43616e6e6f7420696e637265617365207768696c652073756e73657474696e676044820152606401610a92565b6001600160a01b0385165f908152601f602052604090206001600382015460ff1660048111156127875761278761454f565b146127a45760405162461bcd60e51b8152600401610a92906147e2565b80548c156127e8578d156127ce576127bc8c82614836565b90506127c9868d8f613a99565b6127e4565b6127d88c82614823565b90506127e4868e613b5f565b8082555b60018201548a15612839578b1561281b576128038b82614836565b90508a6019546128139190614836565b601955612831565b6128258b82614823565b9050612831878c61303b565b600183018190555b5f61284482846134b6565b905060035f9054906101000a90046001600160a01b03166001600160a01b0316632be212608a838e8e6040518563ffffffff1660e01b815260040161288c94939291906148ae565b5f604051808303815f87803b1580156128a3575f80fd5b505af11580156128b5573d5f803e3d5ffd5b5050505081836128c486613439565b9650965096505050505099509950999650505050505050565b5f805f6128e985612b07565b915091505f6128f9838387613bef565b9695505050505050565b6129136301e133806127106146fb565b61292a676765c793fa10079d601b1b6113886146fb565b6129349190614726565b81565b5f610f95612943612557565b836139ac565b5f60016001600160a01b0383165f908152601f602052604090206003015460ff16600481111561297b5761297b61454f565b1461298757505f919050565b506015546001600160a01b039091165f908152602160205260409020541090565b6129b0613111565b6129ba8383613b5f565b6112d8838261303b565b6129cc613111565b6129d68585613c1d565b60195483156129f1576129e98482614823565b601981905590505b601254601355601d548290612a069083614836565b612a109190614823565b60148190556013546040517f51bf4c63ec3cba9d03d43238abbdd979dd91bd16d9895c74ceea9118c7baaf6092612a4792916142e1565b60405180910390a1604051631062c15f60e11b81526001600160a01b037f000000000000000000000000130541a39cfaa89c9ed8f387d479b277321d1ad4811660048301528881166024830152604482018590527f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f16906320c582be906064015f604051808303815f87803b158015612ade575f80fd5b505af1158015612af0573d5f803e3d5ffd5b50505050612afe878361303b565b50505050505050565b5f80612b1283611c7a565b5090959194509092505050565b5f61103a601054613233565b336001600160a01b037f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd416146113205760405162461bcd60e51b815260206004820152600d60248201526c43616c6c6572206e6f7420424f60981b6044820152606401610a92565b6001600160a01b0381165f908152601f6020526040812081906001600382015460ff166004811115612bc757612bc761454f565b03612cbc5760048101545f612bda612d01565b835460018501549650945090508381831015612c0f5782612bfb83876146fb565b612c059190614726565b6004850183905594505b6015546001600160a01b0388165f908152602160205260409020541015612cad575f80612c3b896125ac565b9092509050612c4a8289614836565b600187018190559750612c5d8188614836565b9650612c68896133c0565b612c72818361324d565b886001600160a01b03165f80516020614a37833981519152888a89600201545f604051612ca29493929190614973565b60405180910390a250505b848114612cb8578484555b5050505b50915091565b5f80612ccc6131f7565b60108190556040518181529091505f80516020614a578339815191529060200160405180910390a1612cfc613d8c565b919050565b5f805f612d0c6132ae565b909250905080156125a657601a545f612d318284676765c793fa10079d601b1b613318565b9050612d3d8183614836565b601a55601b54612d4e908290614836565b601b555050600c82905542600d5550919050565b6001600160a01b0381165f908152601f6020526040902060020154601254612d8b908290614823565b601255506001600160a01b03165f908152601f6020526040812060020155565b6022546001600160a01b0383165f908152601f6020526040902060038101805484919060ff19166001836004811115612de657612de661454f565b02179055505f6001808301829055818355600483018290556003546001600160a01b038781168452602160205260408420848155830193909355919091169083118015612e9257506001816001600160a01b031663de8fa4316040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e6c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e9091906147cb565b115b15612f9757600382015461010090046001600160801b03165f6022612eb8600187614823565b81548110612ec857612ec8614910565b5f91825260209091200154602280546001600160a01b03909216925082916001600160801b038516908110612eff57612eff614910565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918316808252601f83526040918290206003018054610100600160881b0319166101006001600160801b038816908102919091179091558251918252928101929092527f2391d278b0562640dd6add67aa715b4dab2327f1ec896819ff2a809adea29a36910160405180910390a150505b6022805480612fa857612fa8614995565b5f8281526020902081015f1990810180546001600160a01b0319169055019055604051631484968760e11b81526001600160a01b038216906329092d0e90612ff4908890600401614331565b5f604051808303815f87803b15801561300b575f80fd5b505af115801561301d573d5f803e3d5ffd5b50505060039092018054610100600160881b03191690555050505050565b801561130c578060195461304f9190614823565b6019556040517f342693d2465f6f44931e41128424a0227e0cbc69d1c3917a839e6de71696d44c90613084908490849061486a565b60405180910390a160025461130c906001600160a01b031683836131a1565b6022545f0361132057676765c793fa10079d601b1b600c5542600d555f60128190556013819055601481905560158190556016819055601781905560188190556019819055601a819055601d819055601e55565b5f610f95826006546131099190614836565b600754613dfe565b336001600160a01b037f000000000000000000000000fa2de0dcaa6dfdc0d01ad2677258a641bdae084016146113205760405162461bcd60e51b81526020600482015260176024820152762737ba102634b8bab4b230ba34b7b71026b0b730b3b2b960491b6044820152606401610a92565b5f670de0b6b3a764000061319783856146fb565b611c739190614726565b6112d88363a9059cbb60e01b84846040516024016131c092919061486a565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613e13565b5f80613201613ee4565b90505f61321060055483613ef5565b9050670de0b6b3a76400008160105461322991906146fb565b6114aa9190614726565b5f610f95826008546132459190614836565b600954613dfe565b81601e5f82825461325e9190614823565b9250508190555081601a5f8282546132769190614836565b9250508190555080601d5f82825461328e9190614823565b925050819055508060195f8282546114569190614836565b61105b612d01565b600d545f9081904281036132c7575050600c54915f9150565b600b54600c5493508015613312575f6132e08342614823565b90506132ec82826146fb565b93506133048585676765c793fa10079d601b1b613318565b61330e9086614836565b9450505b50509091565b5f80805f19858709858702925082811083820303915050805f0361334f5783828161334557613345614712565b0492505050611c73565b80841161335a575f80fd5b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b60155460165460408051808201825283815260208082018481526001600160a01b0387165f9081526021909252908390209151825551600190910155517fc2910901d6342262f6857ac83c05564d215eef237fb5bcdf69013b47bbf4b8a89061342c90849084906142e1565b60405180910390a1505050565b5f806134488360010154613f9d565b60028401805490829055601254919250905f908390613468908490614823565b6134729190614836565b60128190556040518181529091507f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae5203829060200160405180910390a150909392505050565b5f81156134e257816134d168056bc75e2d63100000856146fb565b6134db9190614726565b9050610f95565b505f19610f95565b5f6001600160a01b03831615806135695750604051630bb7c8fd60e31b81526001600160a01b03851690635dbe47e890613528908690600401614331565b602060405180830381865afa158015613543573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061356791906149a9565b155b806135845750670de0b6b3a764000061358284846128dd565b105b1561359057505f611c73565b60405163765e015960e01b81525f906001600160a01b0386169063765e0159906135be908790600401614331565b602060405180830381865afa1580156135d9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135fd91906147b0565b90506001600160a01b0381161580611c165750670de0b6b3a764000061362382856128dd565b1095945050505050565b61365060405180606001604052805f81526020015f81526020015f151581525090565b6001600160a01b0387165f908152601f60205260409020805461369f90889061369a907f0000000000000000000000000000000000000000000000000de0b6b3a764000090614823565b613dfe565b80835286906136b790670de0b6b3a7640000906146fb565b6136c19190614726565b6020830152815181545f916136d591614823565b90505f836020015183600101546136ec9190614823565b90507f0000000000000000000000000000000000000000000000000de0b6b3a7640000820361378e5761371e8a612d62565b6137298a6004612dab565b6137548a7f0000000000000000000000000000000000000000000000000de0b6b3a764000083613fe3565b896001600160a01b03165f80516020614a378339815191525f805f60036040516137819493929190614973565b60405180910390a261391f565b5f61379982846134b6565b90505f8187116137b2576137ad8783614823565b6137bc565b6137bc8288614823565b90506601c6bf5263400081118061385957507f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd46001600160a01b031663969c24526040518163ffffffff1660e01b8152600401602060405180830381865afa15801561382a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061384e91906147cb565b613857856140d7565b105b156138705750506001604085015250613923915050565b5060405163015f109360e51b81526001600160a01b038d1690632be21260906138a3908e9085908d908d906004016148ae565b5f604051808303815f87803b1580156138ba575f80fd5b505af11580156138cc573d5f803e3d5ffd5b50505083855550600184018290556138e384613439565b508a6001600160a01b03165f80516020614a378339815191528484876002015460036040516139159493929190614973565b60405180910390a2505b5050505b979650505050505050565b5f806139386131f7565b90505f8361394686886146fb565b6139509190614726565b90505f61395e600283614726565b6139689084614836565b905061397c81670de0b6b3a7640000613dfe565b60108190556040518181529091505f80516020614a578339815191529060200160405180910390a16128f9613d8c565b5f80670de0b6b3a76400006139c184866146fb565b6139cb9190614726565b9050828110611c735760405162461bcd60e51b815260206004820152601f60248201527f46656520657863656564732072657475726e656420636f6c6c61746572616c006044820152606401610a92565b5f825f03613a2a575f613a47565b82613a3d670de0b6b3a7640000866146fb565b613a479190614726565b9050818111156110285760405162461bcd60e51b815260206004820152601d60248201527f4665652065786365656465642070726f7669646564206d6178696d756d0000006044820152606401610a92565b5f82601a54613aa89190614836565b9050600a54601e5482613abb9190614836565b1115613ad95760405162461bcd60e51b8152600401610a92906148d9565b601a8190556040516340c10f1960e01b81526001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f16906340c10f1990613b2c908790869060040161486a565b5f604051808303815f87803b158015613b43575f80fd5b505af1158015613b55573d5f803e3d5ffd5b5050505050505050565b604051632770a7eb60e21b81526001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f1690639dc29fac90613bad908590859060040161486a565b5f604051808303815f87803b158015613bc4575f80fd5b505af1158015613bd6573d5f803e3d5ffd5b5050505080601a54613be89190614823565b601a555050565b5f8215613c15575f83613c0284876146fb565b613c0c9190614726565b9150611c739050565b505f19611c73565b815f03613c28575050565b6017545f90613c3f670de0b6b3a7640000846146fb565b613c499190614836565b90505f601854670de0b6b3a764000085613c6391906146fb565b613c6d9190614836565b6012549091505f613c7e8285614726565b90505f613c8b8385614726565b9050613c9783836146fb565b613ca19086614823565b601755613cae83826146fb565b613cb89085614823565b6018556015545f90613ccb908490614836565b90505f82601654613cdc9190614836565b601583905560168190556040519091507f9f8bc8ab0daf5bceef75ecfd2085d1fcc6548c657ea970d9a23a60610d0737e390613d1b90849084906142e1565b60405180910390a188601a5f828254613d349190614823565b9250508190555088601e5f828254613d4c9190614836565b9250508190555087601d5f828254613d649190614836565b925050819055508760195f828254613d7c9190614823565b9091555050505050505050505050565b5f60115442613d9b9190614823565b9050603c811061105b57603c613daf613ee4565b613db991906146fb565b60115f828254613dc99190614836565b90915550506040514281527f860f8d2f0c74dd487e89e2883e3b25b8159ce1e1b3433a291cba7b82c508f3bc9060200161141d565b5f818310613e0c5781611c73565b5090919050565b5f613e67826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166141029092919063ffffffff16565b8051909150156112d85780806020019051810190613e8591906149a9565b6112d85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a92565b5f603c601154426118029190614823565b5f631f540500821115613f0a57631f54050091505b815f03613f205750670de0b6b3a7640000610f95565b670de0b6b3a764000083835b6001811115613f9357613f406002826149c4565b5f03613f6457613f508283614118565b9150613f5d600282614726565b9050613f2c565b613f6e8284614118565b9250613f7a8283614118565b91506002613f89600183614823565b613f5d9190614726565b6128f98284614118565b6014545f908190808203613fb357839150613fdc565b60135480613fc357613fc36149d7565b81613fce82876146fb565b613fd89190614726565b9250505b5092915050565b604051632770a7eb60e21b81526001600160a01b037f0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f1690639dc29fac90614051907f000000000000000000000000130541a39cfaa89c9ed8f387d479b277321d1ad490869060040161486a565b5f604051808303815f87803b158015614068575f80fd5b505af115801561407a573d5f803e3d5ffd5b5050505081601a5461408c9190614823565b601a556001600160a01b0383165f908152602080526040812080548392906140b5908490614836565b925050819055508060195f8282546140cd9190614823565b9091555050505050565b5f610f957f0000000000000000000000000000000000000000000000000de0b6b3a764000083614823565b606061411084845f8561414e565b949350505050565b5f8061412483856146fb565b9050670de0b6b3a764000061413a600282614726565b6141449083614836565b6141109190614726565b6060824710156141af5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a92565b5f80866001600160a01b031685876040516141ca91906149eb565b5f6040518083038185875af1925050503d805f8114614204576040519150601f19603f3d011682016040523d82523d5f602084013e614209565b606091505b509150915061392387838387606083156142835782515f0361427c576001600160a01b0385163b61427c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a92565b5081614110565b61411083838151156142985781518083602001fd5b8060405162461bcd60e51b8152600401610a929190614a01565b6001600160a01b038116811461105b575f80fd5b5f602082840312156142d6575f80fd5b8135611c73816142b2565b918252602082015260400190565b5f610120828403128015614301575f80fd5b509092915050565b801515811461105b575f80fd5b5f60208284031215614326575f80fd5b8135611c7381614309565b6001600160a01b0391909116815260200190565b5f805f8060808587031215614358575f80fd5b8435614363816142b2565b93506020850135614373816142b2565b93969395505050506040820135916060013590565b5f805f6060848603121561439a575f80fd5b83356143a5816142b2565b925060208401356143b5816142b2565b915060408401356143c5816142b2565b809150509250925092565b5f602082840312156143e0575f80fd5b5035919050565b5f80604083850312156143f8575f80fd5b8235614403816142b2565b91506020830135614413816142b2565b809150509250929050565b5f806040838503121561442f575f80fd5b50508035926020909101359150565b5f806040838503121561444f575f80fd5b823561445a816142b2565b946020939093013593505050565b5f805f805f8060c0878903121561447d575f80fd5b8635614488816142b2565b955060208701359450604087013593506060870135925060808701356144ad816142b2565b915060a08701356144bd816142b2565b809150509295509295509295565b93845260208401929092526040830152606082015260800190565b5f805f805f805f60e0888a0312156144fc575f80fd5b87359650602088013561450e816142b2565b9550604088013561451e816142b2565b9450606088013561452e816142b2565b9699959850939660808101359560a0820135955060c0909101359350915050565b634e487b7160e01b5f52602160045260245ffd5b868152602081018690526040810185905260c08101600585106145885761458861454f565b60608201949094526001600160801b0392909216608083015260a0909101529392505050565b5f805f805f805f805f6101208a8c0312156145c7575f80fd5b89356145d281614309565b985060208a0135975060408a0135965060608a01356145f081614309565b955060808a0135945060a08a0135614607816142b2565b935060c08a0135614617816142b2565b925060e08a0135614627816142b2565b91506101008a0135614638816142b2565b809150509295985092959850929598565b5f805f6060848603121561465b575f80fd5b8335614666816142b2565b95602085013595506040909401359392505050565b5f805f805f8060c08789031215614690575f80fd5b863561469b816142b2565b9860208801359850604088013597606081013597506080810135965060a00135945092505050565b6020808252600a908201526927b7363c9037bbb732b960b11b604082015260600190565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610f9557610f956146e7565b634e487b7160e01b5f52601260045260245ffd5b5f8261473457614734614712565b500490565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c0808301359082015260e080830135908201526101208101610100830135614797816142b2565b6001600160a01b03166101009290920191909152919050565b5f602082840312156147c0575f80fd5b8151611c73816142b2565b5f602082840312156147db575f80fd5b5051919050565b60208082526021908201527f506f736974696f6e20636c6f736564206f7220646f6573206e6f7420657869736040820152601d60fa1b606082015260800190565b81810381811115610f9557610f956146e7565b80820180821115610f9557610f956146e7565b5f60208284031215614859575f80fd5b815161ffff81168114611c73575f80fd5b6001600160a01b03929092168252602082015260400190565b60208082526011908201527010dbdb1b185d195c985b0814185d5cd959607a1b604082015260600190565b6001600160a01b03948516815260208101939093529083166040830152909116606082015260800190565b6020808252601d908201527f436f6c6c61746572616c2064656274206c696d69742072656163686564000000604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215614934575f80fd5b81516001600160401b0381168114611c73575f80fd5b5f81614958576149586146e7565b505f190190565b6004811061496f5761496f61454f565b9052565b848152602081018490526040810183905260808101611c16606083018461495f565b634e487b7160e01b5f52603160045260245ffd5b5f602082840312156149b9575f80fd5b8151611c7381614309565b5f826149d2576149d2614712565b500690565b634e487b7160e01b5f52600160045260245ffd5b5f82518060208501845e5f920191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f8301168401019150509291505056fe4fe7fb62190647b8a7596709832f68a365082b18206e55ae330b95593c369affc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600ca264697066735822122014b0870a961a31054827d231caff25708082a1a8f53366c14431671fb561448e64736f6c634300081a0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d57000000000000000000000000130541a39cfaa89c9ed8f387d479b277321d1ad40000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd4000000000000000000000000fa2de0dcaa6dfdc0d01ad2677258a641bdae08400000000000000000000000000000000000000000000000000de0b6b3a7640000

-----Decoded View---------------
Arg [0] : _core (address): 0xF6ec524105548C37D3C2eB482BA197AE19740d57
Arg [1] : _gasPoolAddress (address): 0x130541A39cfaA89c9eD8f387d479B277321D1ad4
Arg [2] : _debtTokenAddress (address): 0x0F26bBb8962d73bC891327F14dB5162D5279899F
Arg [3] : _borrowerOperations (address): 0x49FD0C4fb5172b20b7636b13c49fb15dA52D5bd4
Arg [4] : _liquidationManager (address): 0xfa2dE0DCAa6dfDc0D01aD2677258A641BdAe0840
Arg [5] : _gasCompensation (uint256): 1000000000000000000

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000f6ec524105548c37d3c2eb482ba197ae19740d57
Arg [1] : 000000000000000000000000130541a39cfaa89c9ed8f387d479b277321d1ad4
Arg [2] : 0000000000000000000000000f26bbb8962d73bc891327f14db5162d5279899f
Arg [3] : 00000000000000000000000049fd0c4fb5172b20b7636b13c49fb15da52d5bd4
Arg [4] : 000000000000000000000000fa2de0dcaa6dfdc0d01ad2677258a641bdae0840
Arg [5] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000


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

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.