ETH Price: $3,995.22 (+2.67%)

Contract

0xd366e016Ae0677CdCE93472e603b75051E022AD0
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw Debt213473312024-12-07 1:27:354 hrs ago1733534855IN
0xd366e016...51E022AD0
0 ETH0.0188173814.63120375
Withdraw Coll213427242024-12-06 10:01:3519 hrs ago1733479295IN
0xd366e016...51E022AD0
0 ETH0.0086935918.38840288
Repay Debt213362102024-12-05 12:11:1141 hrs ago1733400671IN
0xd366e016...51E022AD0
0 ETH0.009409120.48218958
Repay Debt213328472024-12-05 0:54:352 days ago1733360075IN
0xd366e016...51E022AD0
0 ETH0.0081500317.47584143
Withdraw Debt213290312024-12-04 12:07:232 days ago1733314043IN
0xd366e016...51E022AD0
0 ETH0.0273249620.33824992
Withdraw Debt213065712024-12-01 8:49:475 days ago1733042987IN
0xd366e016...51E022AD0
0 ETH0.0085895410.24445533
Open Cdp212757902024-11-27 1:24:5910 days ago1732670699IN
0xd366e016...51E022AD0
0 ETH0.006679529.52805012
Close Cdp212757812024-11-27 1:23:1110 days ago1732670591IN
0xd366e016...51E022AD0
0 ETH0.003055769.44848941
Open Cdp212757612024-11-27 1:19:1110 days ago1732670351IN
0xd366e016...51E022AD0
0 ETH0.008094810.60505476
Repay Debt212323182024-11-20 23:46:2316 days ago1732146383IN
0xd366e016...51E022AD0
0 ETH0.004615118.61928673
Withdraw Debt212042102024-11-17 1:41:4720 days ago1731807707IN
0xd366e016...51E022AD0
0 ETH0.0056692111.1
Add Coll212041792024-11-17 1:35:3520 days ago1731807335IN
0xd366e016...51E022AD0
0 ETH0.0069016413.29704807
Repay Debt211951092024-11-15 19:13:5921 days ago1731698039IN
0xd366e016...51E022AD0
0 ETH0.0103892818.71394039
Repay Debt211823262024-11-14 0:22:5923 days ago1731543779IN
0xd366e016...51E022AD0
0 ETH0.0163590424.59314638
Withdraw Debt211544652024-11-10 3:05:3527 days ago1731207935IN
0xd366e016...51E022AD0
0 ETH0.010013779.04818111
Repay Debt211235302024-11-05 19:27:1131 days ago1730834831IN
0xd366e016...51E022AD0
0 ETH0.0067157612.62540189
Close Cdp211021632024-11-02 19:52:1134 days ago1730577131IN
0xd366e016...51E022AD0
0 ETH0.002405926.17121219
Add Coll210866292024-10-31 15:50:4736 days ago1730389847IN
0xd366e016...51E022AD0
0 ETH0.0072852410.73721416
Repay Debt210697242024-10-29 7:13:1138 days ago1730185991IN
0xd366e016...51E022AD0
0 ETH0.004493387.10746579
Close Cdp210682652024-10-29 2:19:4739 days ago1730168387IN
0xd366e016...51E022AD0
0 ETH0.0050396915.4
Close Cdp210682612024-10-29 2:18:5939 days ago1730168339IN
0xd366e016...51E022AD0
0 ETH0.0058153114.6
Withdraw Debt210540842024-10-27 2:49:5941 days ago1729997399IN
0xd366e016...51E022AD0
0 ETH0.003712334.1035039
Close Cdp210360202024-10-24 14:20:5943 days ago1729779659IN
0xd366e016...51E022AD0
0 ETH0.0045304412.87268078
Repay Debt210314452024-10-23 23:01:5944 days ago1729724519IN
0xd366e016...51E022AD0
0 ETH0.003289946.08227911
Repay Debt209874812024-10-17 19:49:4750 days ago1729194587IN
0xd366e016...51E022AD0
0 ETH0.0079035518.43540663
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
194371652024-03-15 1:45:11267 days ago1710467111  Contract Creation0 ETH
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x820d3747...11EdBD910
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
BorrowerOperations

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

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

pragma solidity 0.8.17;

import "./Interfaces/IBorrowerOperations.sol";
import "./Interfaces/ICdpManager.sol";
import "./Interfaces/ICdpManagerData.sol";
import "./Interfaces/IEBTCToken.sol";
import "./Interfaces/ICollSurplusPool.sol";
import "./Interfaces/ISortedCdps.sol";
import "./Dependencies/EbtcBase.sol";
import "./Dependencies/ReentrancyGuard.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/AuthNoOwner.sol";
import "./Dependencies/ERC3156FlashLender.sol";
import "./Dependencies/PermitNonce.sol";

/// @title BorrowerOperations is mainly in charge of all end user interactions like Cdp open, adjust, close etc
/// @notice End users could approve delegate via IPositionManagers for authorized actions on their behalf
/// @dev BorrowerOperations also allows ERC3156 compatible flashmint of eBTC token
contract BorrowerOperations is
    EbtcBase,
    ReentrancyGuard,
    IBorrowerOperations,
    ERC3156FlashLender,
    AuthNoOwner,
    PermitNonce
{
    string public constant NAME = "BorrowerOperations";

    // keccak256("permitPositionManagerApproval(address borrower,address positionManager,uint8 status,uint256 nonce,uint256 deadline)");
    bytes32 private constant _PERMIT_POSITION_MANAGER_TYPEHASH =
        keccak256(
            "PermitPositionManagerApproval(address borrower,address positionManager,uint8 status,uint256 nonce,uint256 deadline)"
        );

    // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
    bytes32 private constant _TYPE_HASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    string internal constant _VERSION = "1";

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
    uint256 private immutable _CACHED_CHAIN_ID;

    bytes32 private immutable _HASHED_NAME;
    bytes32 private immutable _HASHED_VERSION;

    // --- Connected contract declarations ---

    ICdpManager public immutable cdpManager;

    ICollSurplusPool public immutable collSurplusPool;

    address public immutable feeRecipientAddress;

    IEBTCToken public immutable ebtcToken;

    // A doubly linked list of Cdps, sorted by their collateral ratios
    ISortedCdps public immutable sortedCdps;

    // Mapping of borrowers to approved position managers, by approval status: cdpOwner(borrower) -> positionManager -> PositionManagerApproval (None, OneTime, Persistent)
    mapping(address => mapping(address => PositionManagerApproval)) public positionManagers;

    /* --- Variable container structs  ---

    Used to hold, return and assign variables inside a function, in order to avoid the error:
    "CompilerError: Stack too deep". */

    struct AdjustCdpLocals {
        uint256 price;
        uint256 collSharesChange;
        uint256 netDebtChange;
        bool isCollIncrease;
        uint256 debt;
        uint256 collShares;
        uint256 oldICR;
        uint256 newICR;
        uint256 newTCR;
        uint256 newDebt;
        uint256 newCollShares;
        uint256 stake;
    }

    struct OpenCdpLocals {
        uint256 price;
        uint256 debt;
        uint256 netStEthBalance;
        uint256 ICR;
        uint256 NICR;
        uint256 stake;
    }

    struct MoveTokensParams {
        address user;
        uint256 collSharesChange;
        uint256 collAddUnderlying; // ONLY for isCollIncrease=true
        bool isCollIncrease;
        uint256 netDebtChange;
        bool isDebtIncrease;
    }

    // --- Dependency setters ---
    constructor(
        address _cdpManagerAddress,
        address _activePoolAddress,
        address _collSurplusPoolAddress,
        address _priceFeedAddress,
        address _sortedCdpsAddress,
        address _ebtcTokenAddress,
        address _feeRecipientAddress,
        address _collTokenAddress
    ) EbtcBase(_activePoolAddress, _priceFeedAddress, _collTokenAddress) {
        cdpManager = ICdpManager(_cdpManagerAddress);
        collSurplusPool = ICollSurplusPool(_collSurplusPoolAddress);
        sortedCdps = ISortedCdps(_sortedCdpsAddress);
        ebtcToken = IEBTCToken(_ebtcTokenAddress);
        feeRecipientAddress = _feeRecipientAddress;

        address _authorityAddress = address(AuthNoOwner(_cdpManagerAddress).authority());
        if (_authorityAddress != address(0)) {
            _initializeAuthority(_authorityAddress);
        }

        bytes32 hashedName = keccak256(bytes(NAME));
        bytes32 hashedVersion = keccak256(bytes(_VERSION));

        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
        _CACHED_CHAIN_ID = _chainID();
        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(_TYPE_HASH, hashedName, hashedVersion);
    }

    /**
        @notice BorrowerOperations and CdpManager share reentrancy status by confirming the other's locked flag before beginning operation
        @dev This is an alternative to the more heavyweight solution of both being able to set the reentrancy flag on a 3rd contract.
        @dev Prevents multi-contract reentrancy between these two contracts
     */
    modifier nonReentrantSelfAndCdpM() {
        require(locked == OPEN, "BorrowerOperations: Reentrancy in nonReentrant call");
        require(
            ReentrancyGuard(address(cdpManager)).locked() == OPEN,
            "CdpManager: Reentrancy in nonReentrant call"
        );

        locked = LOCKED;

        _;

        locked = OPEN;
    }

    // --- Borrower Cdp Operations ---

    /// @notice Function that creates a Cdp for the caller with the requested debt, and the stETH received as collateral.
    /// @notice Successful execution is conditional mainly on the resulting collateralization ratio which must exceed minimum requirement, e.g., MCR.
    /// @notice Upon Cdp open, a separate gas stipend (denominated in stETH) will be allocated for possible liquidation.
    /// @param _debt The expected debt for this new Cdp
    /// @param _upperHint The expected CdpId of neighboring higher ICR within SortedCdps, could be simply bytes32(0)
    /// @param _lowerHint The expected CdpId of neighboring lower ICR within SortedCdps, could be simply bytes32(0)
    /// @param _stEthBalance The total stETH collateral amount deposited for the specified Cdp
    /// @return The CdpId for this newly created Cdp
    function openCdp(
        uint256 _debt,
        bytes32 _upperHint,
        bytes32 _lowerHint,
        uint256 _stEthBalance
    ) external override nonReentrantSelfAndCdpM returns (bytes32) {
        return _openCdp(_debt, _upperHint, _lowerHint, _stEthBalance, msg.sender);
    }

    /// @notice Function that creates a Cdp for the specified _borrower by caller with the requested debt, and the stETH received as collateral.
    /// @dev Caller will need approval from _borrower via IPositionManagers if they are different address
    /// @notice Successful execution is conditional mainly on the resulting collateralization ratio which must exceed minimum requirement, e.g., MCR.
    /// @notice Upon Cdp open, a separate gas stipend (denominated in stETH) will be allocated for possible liquidation.
    /// @param _debt The expected debt for this new Cdp
    /// @param _upperHint The expected CdpId of neighboring higher ICR within SortedCdps, could be simply bytes32(0)
    /// @param _lowerHint The expected CdpId of neighboring lower ICR within SortedCdps, could be simply bytes32(0)
    /// @param _stEthBalance The total stETH collateral amount deposited for the specified Cdp
    /// @param _borrower The Cdp owner for this new Cdp.
    /// @return The CdpId for this newly created Cdp
    function openCdpFor(
        uint256 _debt,
        bytes32 _upperHint,
        bytes32 _lowerHint,
        uint256 _stEthBalance,
        address _borrower
    ) external override nonReentrantSelfAndCdpM returns (bytes32) {
        return _openCdp(_debt, _upperHint, _lowerHint, _stEthBalance, _borrower);
    }

    /// @notice Function that adds the received stETH to the specified Cdp.
    /// @dev If caller is different from Cdp owner, it will need approval from Cdp owner for this call
    /// @param _cdpId The CdpId on which this operation is operated
    /// @param _upperHint The expected CdpId of neighboring higher ICR within SortedCdps, could be simply bytes32(0)
    /// @param _lowerHint The expected CdpId of neighboring lower ICR within SortedCdps, could be simply bytes32(0)
    /// @param _stEthBalanceIncrease The total stETH collateral amount deposited (added) for the specified Cdp
    function addColl(
        bytes32 _cdpId,
        bytes32 _upperHint,
        bytes32 _lowerHint,
        uint256 _stEthBalanceIncrease
    ) external override nonReentrantSelfAndCdpM {
        _adjustCdpInternal(_cdpId, 0, 0, false, _upperHint, _lowerHint, _stEthBalanceIncrease);
    }

    /// @notice Function that withdraws `_stEthBalanceDecrease` amount of collateral from the specified Cdp
    /// @dev If caller is different from Cdp owner, it will need approval from Cdp owner for this call
    /// @notice Successful execution is conditional on whether the withdrawal would bring down the ICR or TCR to the minimum requirement, e.g., MCR or CCR
    /// @param _cdpId The CdpId on which this operation is operated
    /// @param _stEthBalanceDecrease The total stETH collateral amount withdrawn (reduced) for the specified Cdp
    /// @param _upperHint The expected CdpId of neighboring higher ICR within SortedCdps, could be simply bytes32(0)
    /// @param _lowerHint The expected CdpId of neighboring lower ICR within SortedCdps, could be simply bytes32(0)
    function withdrawColl(
        bytes32 _cdpId,
        uint256 _stEthBalanceDecrease,
        bytes32 _upperHint,
        bytes32 _lowerHint
    ) external override nonReentrantSelfAndCdpM {
        _adjustCdpInternal(_cdpId, _stEthBalanceDecrease, 0, false, _upperHint, _lowerHint, 0);
    }

    /// @notice Function that withdraws `_debt` amount of eBTC token from the specified Cdp, thus increasing its debt accounting
    /// @dev If caller is different from Cdp owner, it will need approval from Cdp owner for this call
    /// @notice Successful execution is conditional on whether the withdrawal would bring down the ICR or TCR to the minimum requirement, e.g., MCR or CCR
    /// @param _cdpId The CdpId on which this operation is operated
    /// @param _debt The total debt collateral amount increased for the specified Cdp
    /// @param _upperHint The expected CdpId of neighboring higher ICR within SortedCdps, could be simply bytes32(0)
    /// @param _lowerHint The expected CdpId of neighboring lower ICR within SortedCdps, could be simply bytes32(0)
    function withdrawDebt(
        bytes32 _cdpId,
        uint256 _debt,
        bytes32 _upperHint,
        bytes32 _lowerHint
    ) external override nonReentrantSelfAndCdpM {
        _adjustCdpInternal(_cdpId, 0, _debt, true, _upperHint, _lowerHint, 0);
    }

    /// @notice Function that repays the received eBTC token to the specified Cdp, thus reducing its debt accounting.
    /// @dev If caller is different from Cdp owner, it will need approval from Cdp owner for this call
    /// @param _cdpId The CdpId on which this operation is operated
    /// @param _upperHint The expected CdpId of neighboring higher ICR within SortedCdps, could be simply bytes32(0)
    /// @param _lowerHint The expected CdpId of neighboring lower ICR within SortedCdps, could be simply bytes32(0)
    /// @param _debt The total eBTC debt amount repaid for the specified Cdp
    function repayDebt(
        bytes32 _cdpId,
        uint256 _debt,
        bytes32 _upperHint,
        bytes32 _lowerHint
    ) external override nonReentrantSelfAndCdpM {
        _adjustCdpInternal(_cdpId, 0, _debt, false, _upperHint, _lowerHint, 0);
    }

    /// @notice Function that allows various operations which might change both collateral and debt
    /// @notice like taking more risky position (withdraws eBTC token and reduces stETH collateral)
    /// @notice or holding more safer position (repays eBTC token) with the specified Cdp.
    /// @notice If end user want to add collateral and change debt at the same time, use adjustCdpWithColl() instead
    /// @dev If caller is different from Cdp owner, it will need approval from Cdp owner for this call
    /// @param _cdpId The CdpId on which this operation is operated
    /// @param _stEthBalanceDecrease The total stETH collateral amount withdrawn from the specified Cdp
    /// @param _debtChange The total eBTC debt amount withdrawn or repaid for the specified Cdp
    /// @param _isDebtIncrease The flag (true or false) to indicate whether this is a eBTC token withdrawal (debt increase) or a repayment (debt reduce)
    /// @param _upperHint The expected CdpId of neighboring higher ICR within SortedCdps, could be simply bytes32(0)
    /// @param _lowerHint The expected CdpId of neighboring lower ICR within SortedCdps, could be simply bytes32(0)
    function adjustCdp(
        bytes32 _cdpId,
        uint256 _stEthBalanceDecrease,
        uint256 _debtChange,
        bool _isDebtIncrease,
        bytes32 _upperHint,
        bytes32 _lowerHint
    ) external override nonReentrantSelfAndCdpM {
        _adjustCdpInternal(
            _cdpId,
            _stEthBalanceDecrease,
            _debtChange,
            _isDebtIncrease,
            _upperHint,
            _lowerHint,
            0
        );
    }

    /// @notice Function that allows various operations which might change both collateral and debt
    /// @notice like taking more risky position (withdraws eBTC token and reduces stETH collateral)
    /// @notice or holding more safer position (repays eBTC token and adds stETH collateral) with the specified Cdp.
    /// @dev If caller is different from Cdp owner, it will need approval from Cdp owner for this call
    /// @param _cdpId The CdpId on which this operation is operated
    /// @param _stEthBalanceDecrease The total stETH collateral amount withdrawn from the specified Cdp
    /// @param _debtChange The total eBTC debt amount withdrawn or repaid for the specified Cdp
    /// @param _isDebtIncrease The flag (true or false) to indicate whether this is a eBTC token withdrawal (debt increase) or a repayment (debt reduce)
    /// @param _upperHint The expected CdpId of neighboring higher ICR within SortedCdps, could be simply bytes32(0)
    /// @param _lowerHint The expected CdpId of neighboring lower ICR within SortedCdps, could be simply bytes32(0)
    /// @param _stEthBalanceIncrease The total stETH collateral amount deposited (added) for the specified Cdp
    function adjustCdpWithColl(
        bytes32 _cdpId,
        uint256 _stEthBalanceDecrease,
        uint256 _debtChange,
        bool _isDebtIncrease,
        bytes32 _upperHint,
        bytes32 _lowerHint,
        uint256 _stEthBalanceIncrease
    ) external override nonReentrantSelfAndCdpM {
        _adjustCdpInternal(
            _cdpId,
            _stEthBalanceDecrease,
            _debtChange,
            _isDebtIncrease,
            _upperHint,
            _lowerHint,
            _stEthBalanceIncrease
        );
    }

    /*
     * _adjustCdpInternal(): Alongside a debt change, this function can perform either
     * a collateral top-up or a collateral withdrawal.
     *
     * It therefore expects either a positive _stEthBalanceIncrease, or a positive _stEthBalanceDecrease argument.
     *
     * If both are positive, it will revert.
     */
    function _adjustCdpInternal(
        bytes32 _cdpId,
        uint256 _stEthBalanceDecrease,
        uint256 _debtChange,
        bool _isDebtIncrease,
        bytes32 _upperHint,
        bytes32 _lowerHint,
        uint256 _stEthBalanceIncrease
    ) internal {
        // Confirm the operation is the borrower or approved position manager adjusting its own cdp
        address _borrower = sortedCdps.getOwnerAddress(_cdpId);
        _requireBorrowerOrPositionManagerAndUpdateManagerApproval(_borrower);

        _requireCdpisActive(cdpManager, _cdpId);

        cdpManager.syncAccounting(_cdpId);

        AdjustCdpLocals memory vars;

        vars.price = priceFeed.fetchPrice();

        if (_isDebtIncrease) {
            _requireMinDebtChange(_debtChange);
        } else {
            _requireZeroOrMinAdjustment(_debtChange);
        }

        _requireSingularCollChange(_stEthBalanceIncrease, _stEthBalanceDecrease);
        _requireNonZeroAdjustment(_stEthBalanceIncrease, _stEthBalanceDecrease, _debtChange);
        _requireZeroOrMinAdjustment(_stEthBalanceIncrease);
        _requireZeroOrMinAdjustment(_stEthBalanceDecrease);
        // min debt adjustment checked above

        // Get the collSharesChange based on the collateral value transferred in the transaction
        (vars.collSharesChange, vars.isCollIncrease) = _getCollSharesChangeFromStEthChange(
            _stEthBalanceIncrease,
            _stEthBalanceDecrease
        );

        vars.netDebtChange = _debtChange;

        vars.debt = cdpManager.getCdpDebt(_cdpId);
        vars.collShares = cdpManager.getCdpCollShares(_cdpId);

        // Get the cdp's old ICR before the adjustment, and what its new ICR will be after the adjustment
        uint256 _cdpStEthBalance = collateral.getPooledEthByShares(vars.collShares);
        require(
            _stEthBalanceDecrease <= _cdpStEthBalance,
            "BorrowerOperations: Cannot withdraw greater stEthBalance than the value in Cdp"
        );
        vars.oldICR = EbtcMath._computeCR(_cdpStEthBalance, vars.debt, vars.price);
        vars.newICR = _getNewICRFromCdpChange(
            vars.collShares,
            vars.debt,
            vars.collSharesChange,
            vars.isCollIncrease,
            vars.netDebtChange,
            _isDebtIncrease,
            vars.price
        );

        // Check the adjustment satisfies all conditions for the current system mode
        bool isRecoveryMode = _checkRecoveryModeForTCR(_getCachedTCR(vars.price));
        _requireValidAdjustmentInCurrentMode(
            isRecoveryMode,
            _stEthBalanceDecrease,
            _isDebtIncrease,
            vars
        );

        // When the adjustment is a debt repayment, check it's a valid amount, that the caller has enough EBTC, and that the resulting debt is >0
        if (!_isDebtIncrease && _debtChange > 0) {
            _requireValidDebtRepayment(vars.debt, vars.netDebtChange);
            _requireSufficientEbtcTokenBalance(msg.sender, vars.netDebtChange);
            _requireMinDebt(vars.debt - vars.netDebtChange);
        }

        (vars.newCollShares, vars.newDebt) = _getNewCdpAmounts(
            vars.collShares,
            vars.debt,
            vars.collSharesChange,
            vars.isCollIncrease,
            vars.netDebtChange,
            _isDebtIncrease
        );

        _requireMinDebt(vars.newDebt);
        _requireAtLeastMinNetStEthBalance(collateral.getPooledEthByShares(vars.newCollShares));

        cdpManager.updateCdp(
            _cdpId,
            _borrower,
            vars.collShares,
            vars.debt,
            vars.newCollShares,
            vars.newDebt
        );

        // Re-insert cdp in to the sorted list
        {
            uint256 newNICR = _getNewNominalICRFromCdpChange(vars, _isDebtIncrease);
            sortedCdps.reInsert(_cdpId, newNICR, _upperHint, _lowerHint);
        }

        // CEI: Process token movements
        {
            MoveTokensParams memory _varMvTokens = MoveTokensParams(
                msg.sender,
                vars.collSharesChange,
                (vars.isCollIncrease ? _stEthBalanceIncrease : 0),
                vars.isCollIncrease,
                _debtChange,
                _isDebtIncrease
            );
            _processTokenMovesFromAdjustment(_varMvTokens);
        }
    }

    function _openCdp(
        uint256 _debt,
        bytes32 _upperHint,
        bytes32 _lowerHint,
        uint256 _stEthBalance,
        address _borrower
    ) internal returns (bytes32) {
        _requireMinDebt(_debt);
        _requireBorrowerOrPositionManagerAndUpdateManagerApproval(_borrower);

        OpenCdpLocals memory vars;

        // ICR is based on the net stEth balance, i.e. the specified stEth balance amount - fixed liquidator incentive gas comp.
        vars.netStEthBalance = _calcNetStEthBalance(_stEthBalance);

        _requireAtLeastMinNetStEthBalance(vars.netStEthBalance);

        // Update global pending index before any operations
        cdpManager.syncGlobalAccounting();

        vars.price = priceFeed.fetchPrice();
        vars.debt = _debt;

        // Sanity check
        require(vars.netStEthBalance > 0, "BorrowerOperations: zero collateral for openCdp()!");

        uint256 _netCollAsShares = collateral.getSharesByPooledEth(vars.netStEthBalance);
        uint256 _liquidatorRewardShares = collateral.getSharesByPooledEth(LIQUIDATOR_REWARD);

        // ICR is based on the net coll, i.e. the requested coll amount - fixed liquidator incentive gas comp.
        vars.ICR = EbtcMath._computeCR(vars.netStEthBalance, vars.debt, vars.price);

        // NICR uses shares to normalize NICR across Cdps opened at different pooled ETH / shares ratios
        vars.NICR = EbtcMath._computeNominalCR(_netCollAsShares, vars.debt);

        /**
            In recovery move, ICR must be greater than CCR
            CCR > MCR (125% vs 110%)

            In normal mode, ICR must be greater thatn MCR
            Additionally, the new system TCR after the Cdps addition must be >CCR
        */
        bool isRecoveryMode = _checkRecoveryModeForTCR(_getCachedTCR(vars.price));
        uint256 newTCR = _getNewTCRFromCdpChange(
            vars.netStEthBalance,
            true,
            vars.debt,
            true,
            vars.price
        );
        if (isRecoveryMode) {
            _requireICRisNotBelowCCR(vars.ICR);

            // == Grace Period == //
            // We are in RM, Edge case is Depositing Coll could exit RM
            // We check with newTCR
            if (newTCR < CCR) {
                // Notify RM
                cdpManager.notifyStartGracePeriod(newTCR);
            } else {
                // Notify Back to Normal Mode
                cdpManager.notifyEndGracePeriod(newTCR);
            }
        } else {
            _requireICRisNotBelowMCR(vars.ICR);
            _requireNewTCRisNotBelowCCR(newTCR);

            // == Grace Period == //
            // We are not in RM, no edge case, we always stay above RM
            // Always Notify Back to Normal Mode
            cdpManager.notifyEndGracePeriod(newTCR);
        }

        // Set the cdp struct's properties
        bytes32 _cdpId = sortedCdps.insert(_borrower, vars.NICR, _upperHint, _lowerHint);

        // Collision check: collisions should never occur
        // Explicitly prevent it by checking for `nonExistent`
        _requireCdpIsNonExistent(_cdpId);

        // Collateral is stored in shares form for normalization
        cdpManager.initializeCdp(
            _cdpId,
            vars.debt,
            _netCollAsShares,
            _liquidatorRewardShares,
            _borrower
        );

        // CEI: Mint the full debt amount, in eBTC tokens, to the caller
        _withdrawDebt(msg.sender, _debt);

        /**
            Note that only NET stEth balance (as shares) is considered part of the Cdp.
            The static liqudiation incentive is stored in the gas pool and can be considered a deposit / voucher to be returned upon Cdp close, to the closer.
            The close can happen from the borrower closing their own Cdp, a full liquidation, or a redemption.
        */

        // CEI: Move the collateral and liquidator gas compensation to the Active Pool. Track only net collateral for TCR purposes.
        _activePoolAddColl(_stEthBalance, _netCollAsShares);

        // Invariant check
        require(
            vars.netStEthBalance + LIQUIDATOR_REWARD == _stEthBalance,
            "BorrowerOperations: deposited collateral mismatch!"
        );

        return _cdpId;
    }

    /// @notice Function that allows the caller to repay all debt, withdraw collateral, and close the specified Cdp
    /// @notice Caller should have enough eBTC token to repay off the debt fully for specified Cdp
    /// @dev If caller is different from Cdp owner, it will need approval from Cdp owner for this call
    /// @param _cdpId The CdpId on which this operation is operated
    function closeCdp(bytes32 _cdpId) external override {
        address _borrower = sortedCdps.getOwnerAddress(_cdpId);
        _requireBorrowerOrPositionManagerAndUpdateManagerApproval(_borrower);

        _requireCdpisActive(cdpManager, _cdpId);

        cdpManager.syncAccounting(_cdpId);

        uint256 price = priceFeed.fetchPrice();
        _requireNotInRecoveryMode(_getCachedTCR(price));

        uint256 collShares = cdpManager.getCdpCollShares(_cdpId);
        uint256 debt = cdpManager.getCdpDebt(_cdpId);
        uint256 liquidatorRewardShares = cdpManager.getCdpLiquidatorRewardShares(_cdpId);

        _requireSufficientEbtcTokenBalance(msg.sender, debt);

        uint256 newTCR = _getNewTCRFromCdpChange(
            collateral.getPooledEthByShares(collShares),
            false,
            debt,
            false,
            price
        );
        _requireNewTCRisNotBelowCCR(newTCR);

        // == Grace Period == //
        // By definition we are not in RM, notify CDPManager to ensure "Glass is on"
        cdpManager.notifyEndGracePeriod(newTCR);

        cdpManager.closeCdp(_cdpId, _borrower, debt, collShares);

        // Burn the repaid EBTC from the user's balance
        _repayDebt(msg.sender, debt);

        // CEI: Send the collateral and liquidator reward shares back to the user
        activePool.transferSystemCollSharesAndLiquidatorReward(
            msg.sender,
            collShares,
            liquidatorRewardShares
        );
    }

    /// @notice Claim remaining collateral from a redemption or from a liquidation with ICR > MCR in Recovery Mode
    /// @notice when a Cdp has been fully redeemed from and closed, or liquidated in Recovery Mode with a collateralization ratio higher enough (like over MCR)
    /// @notice the borrower is allowed to claim their stETH collateral surplus that remains in the system if any
    function claimSurplusCollShares() external override {
        // send ETH from CollSurplus Pool to owner
        collSurplusPool.claimSurplusCollShares(msg.sender);
    }

    /// @notice Returns true if the borrower is allowing position manager to act on their behalf
    /// @return PositionManagerApproval (None/OneTime/Persistent) status for given _borrower and _positionManager
    /// @param _borrower The Cdp owner who use eBTC
    /// @param _positionManager The position manager address in question whether it gets valid approval from _borrower
    function getPositionManagerApproval(
        address _borrower,
        address _positionManager
    ) external view override returns (PositionManagerApproval) {
        return _getPositionManagerApproval(_borrower, _positionManager);
    }

    function _getPositionManagerApproval(
        address _borrower,
        address _positionManager
    ) internal view returns (PositionManagerApproval) {
        return positionManagers[_borrower][_positionManager];
    }

    /// @notice Approve an account (_positionManager) to take arbitrary actions on your Cdps.
    /// @notice Position managers with 'Persistent' status will be able to take actions indefinitely
    /// @notice Position managers with 'OneTIme' status will be able to take a single action on one Cdp. Approval will be automatically revoked after one Cdp-related action.
    /// @notice Similar to approving tokens, approving a position manager allows _stealing of all positions_ if given to a malicious account.
    /// @param _positionManager The position manager address which will get the specified approval from caller
    /// @param _approval PositionManagerApproval (None/OneTime/Persistent) status set to the specified _positionManager for caller's Cdp
    function setPositionManagerApproval(
        address _positionManager,
        PositionManagerApproval _approval
    ) external override {
        _setPositionManagerApproval(msg.sender, _positionManager, _approval);
    }

    function _setPositionManagerApproval(
        address _borrower,
        address _positionManager,
        PositionManagerApproval _approval
    ) internal {
        positionManagers[_borrower][_positionManager] = _approval;
        emit PositionManagerApprovalSet(_borrower, _positionManager, _approval);
    }

    /// @notice Revoke a position manager from taking further actions on your Cdps
    /// @notice Similar to approving tokens, approving a position manager allows _stealing of all positions_ if given to a malicious account.
    /// @param _positionManager The position manager address which will get all approval revoked by caller (a Cdp owner)
    function revokePositionManagerApproval(address _positionManager) external override {
        _setPositionManagerApproval(msg.sender, _positionManager, PositionManagerApproval.None);
    }

    /// @notice Allows recipient of delegation to renounce it
    /// @param _borrower The Cdp owner address which will have all approval to the caller (a PositionManager) revoked.
    function renouncePositionManagerApproval(address _borrower) external override {
        _setPositionManagerApproval(_borrower, msg.sender, PositionManagerApproval.None);
    }

    /// @notice This function returns the domain separator for current chain
    /// @return EIP712 compatible Domain definition
    function DOMAIN_SEPARATOR() external view returns (bytes32) {
        return domainSeparator();
    }

    /// @notice This function returns the domain separator for current chain
    /// @return EIP712 compatible Domain definition
    function domainSeparator() public view override returns (bytes32) {
        if (_chainID() == _CACHED_CHAIN_ID) {
            return _CACHED_DOMAIN_SEPARATOR;
        } else {
            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
        }
    }

    function _chainID() private view returns (uint256) {
        return block.chainid;
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 name,
        bytes32 version
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, name, version, _chainID(), address(this)));
    }

    /// @notice This function returns the version parameter for the EIP712 domain
    /// @return EIP712 compatible version parameter
    function version() external pure override returns (string memory) {
        return _VERSION;
    }

    /// @notice This function returns hash of the fully encoded EIP712 message for the permitPositionManagerApproval.
    /// @return EIP712 compatible hash of Positon Manager permit
    function permitTypeHash() external pure override returns (bytes32) {
        return _PERMIT_POSITION_MANAGER_TYPEHASH;
    }

    /// @notice This function set given _approval for specified _borrower and _positionManager
    /// @notice by verifying the validity of given deadline and signature parameters (v, r, s).
    /// @param _borrower The Cdp owner
    /// @param _positionManager The delegate to which _borrower want to grant approval
    /// @param _approval The PositionManagerApproval (None/OneTime/Persistent) status to be set
    /// @param _deadline The permit valid deadline
    /// @param v The v part of signature from _borrower
    /// @param r The r part of signature from _borrower
    /// @param s The s part of signature from _borrower
    function permitPositionManagerApproval(
        address _borrower,
        address _positionManager,
        PositionManagerApproval _approval,
        uint256 _deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external override {
        require(_deadline >= block.timestamp, "BorrowerOperations: Position manager permit expired");

        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                domainSeparator(),
                keccak256(
                    abi.encode(
                        _PERMIT_POSITION_MANAGER_TYPEHASH,
                        _borrower,
                        _positionManager,
                        _approval,
                        _nonces[_borrower]++,
                        _deadline
                    )
                )
            )
        );
        address recoveredAddress = ecrecover(digest, v, r, s);
        require(
            recoveredAddress != address(0) && recoveredAddress == _borrower,
            "BorrowerOperations: Invalid signature"
        );

        _setPositionManagerApproval(_borrower, _positionManager, _approval);
    }

    // --- Helper functions ---

    function _getCollSharesChangeFromStEthChange(
        uint256 _collReceived,
        uint256 _requestedCollWithdrawal
    ) internal view returns (uint256 collSharesChange, bool isCollIncrease) {
        if (_collReceived != 0) {
            collSharesChange = collateral.getSharesByPooledEth(_collReceived);
            isCollIncrease = true;
        } else {
            collSharesChange = collateral.getSharesByPooledEth(_requestedCollWithdrawal);
        }
    }

    /**
        @notice Process the token movements required by a Cdp adjustment.
        @notice Handles the cases of a debt increase / decrease, and/or a collateral increase / decrease.
     */
    function _processTokenMovesFromAdjustment(MoveTokensParams memory _varMvTokens) internal {
        // Debt increase: mint change value of new eBTC to user, increment ActivePool eBTC internal accounting
        if (_varMvTokens.isDebtIncrease) {
            _withdrawDebt(_varMvTokens.user, _varMvTokens.netDebtChange);
        } else {
            // Debt decrease: burn change value of eBTC from user, decrement ActivePool eBTC internal accounting
            _repayDebt(_varMvTokens.user, _varMvTokens.netDebtChange);
        }

        if (_varMvTokens.isCollIncrease) {
            // Coll increase: send change value of stETH to Active Pool, increment ActivePool stETH internal accounting
            _activePoolAddColl(_varMvTokens.collAddUnderlying, _varMvTokens.collSharesChange);
        } else {
            // Coll decrease: send change value of stETH to user, decrement ActivePool stETH internal accounting
            activePool.transferSystemCollShares(_varMvTokens.user, _varMvTokens.collSharesChange);
        }
    }

    /// @notice Send stETH to Active Pool and increase its recorded ETH balance
    /// @param _stEthBalance total balance of stETH to send, inclusive of coll and liquidatorRewardShares
    /// @param _sharesToTrack coll as shares (exclsuive of liquidator reward shares)
    /// @dev Liquidator reward shares are not considered as part of the system for CR purposes.
    /// @dev These number of liquidator shares associated with each Cdp are stored in the Cdp, while the actual tokens float in the active pool
    function _activePoolAddColl(uint256 _stEthBalance, uint256 _sharesToTrack) internal {
        // NOTE: No need for safe transfer if the collateral asset is standard. Make sure this is the case!
        collateral.transferFrom(msg.sender, address(activePool), _stEthBalance);
        activePool.increaseSystemCollShares(_sharesToTrack);
    }

    /// @dev Mint specified debt tokens to account and change global debt accounting accordingly
    function _withdrawDebt(address _account, uint256 _debt) internal {
        activePool.increaseSystemDebt(_debt);
        ebtcToken.mint(_account, _debt);
    }

    // Burn the specified amount of EBTC from _account and decreases the total active debt
    function _repayDebt(address _account, uint256 _debt) internal {
        activePool.decreaseSystemDebt(_debt);
        ebtcToken.burn(_account, _debt);
    }

    // --- 'Require' wrapper functions ---

    function _requireSingularCollChange(
        uint256 _stEthBalanceIncrease,
        uint256 _stEthBalanceDecrease
    ) internal pure {
        require(
            _stEthBalanceIncrease == 0 || _stEthBalanceDecrease == 0,
            "BorrowerOperations: Cannot add and withdraw collateral in same operation"
        );
    }

    function _requireNonZeroAdjustment(
        uint256 _stEthBalanceIncrease,
        uint256 _debtChange,
        uint256 _stEthBalanceDecrease
    ) internal pure {
        require(
            _stEthBalanceIncrease > 0 || _stEthBalanceDecrease > 0 || _debtChange > 0,
            "BorrowerOperations: There must be either a collateral or debt change"
        );
    }

    function _requireZeroOrMinAdjustment(uint256 _change) internal pure {
        require(
            _change == 0 || _change >= MIN_CHANGE,
            "BorrowerOperations: Collateral or debt change must be zero or above min"
        );
    }

    function _requireCdpisActive(ICdpManager _cdpManager, bytes32 _cdpId) internal view {
        uint256 status = _cdpManager.getCdpStatus(_cdpId);
        require(status == 1, "BorrowerOperations: Cdp does not exist or is closed");
    }

    function _requireCdpIsNonExistent(bytes32 _cdpId) internal view {
        uint status = cdpManager.getCdpStatus(_cdpId);
        require(status == 0, "BorrowerOperations: Cdp is active or has been previously closed");
    }

    function _requireMinDebtChange(uint _debtChange) internal pure {
        require(
            _debtChange >= MIN_CHANGE,
            "BorrowerOperations: Debt increase requires min debtChange"
        );
    }

    function _requireNotInRecoveryMode(uint256 _tcr) internal view {
        require(
            !_checkRecoveryModeForTCR(_tcr),
            "BorrowerOperations: Operation not permitted during Recovery Mode"
        );
    }

    function _requireNoStEthBalanceDecrease(uint256 _stEthBalanceDecrease) internal pure {
        require(
            _stEthBalanceDecrease == 0,
            "BorrowerOperations: Collateral withdrawal not permitted during Recovery Mode"
        );
    }

    function _requireValidAdjustmentInCurrentMode(
        bool _isRecoveryMode,
        uint256 _stEthBalanceDecrease,
        bool _isDebtIncrease,
        AdjustCdpLocals memory _vars
    ) internal {
        /*
         *In Recovery Mode, only allow:
         *
         * - Pure collateral top-up
         * - Pure debt repayment
         * - Collateral top-up with debt repayment
         * - A debt increase combined with a collateral top-up which makes the
         * ICR >= 150% and improves the ICR (and by extension improves the TCR).
         *
         * In Normal Mode, ensure:
         *
         * - The new ICR is above MCR
         * - The adjustment won't pull the TCR below CCR
         */

        _vars.newTCR = _getNewTCRFromCdpChange(
            collateral.getPooledEthByShares(_vars.collSharesChange),
            _vars.isCollIncrease,
            _vars.netDebtChange,
            _isDebtIncrease,
            _vars.price
        );

        if (_isRecoveryMode) {
            _requireNoStEthBalanceDecrease(_stEthBalanceDecrease);
            if (_isDebtIncrease) {
                _requireICRisNotBelowCCR(_vars.newICR);
                _requireNoDecreaseOfICR(_vars.newICR, _vars.oldICR);
            }

            // == Grace Period == //
            // We are in RM, Edge case is Depositing Coll could exit RM
            // We check with newTCR
            if (_vars.newTCR < CCR) {
                // Notify RM
                cdpManager.notifyStartGracePeriod(_vars.newTCR);
            } else {
                // Notify Back to Normal Mode
                cdpManager.notifyEndGracePeriod(_vars.newTCR);
            }
        } else {
            // if Normal Mode
            _requireICRisNotBelowMCR(_vars.newICR);
            _requireNewTCRisNotBelowCCR(_vars.newTCR);

            // == Grace Period == //
            // We are not in RM, no edge case, we always stay above RM
            // Always Notify Back to Normal Mode
            cdpManager.notifyEndGracePeriod(_vars.newTCR);
        }
    }

    function _requireICRisNotBelowMCR(uint256 _newICR) internal pure {
        require(
            _newICR >= MCR,
            "BorrowerOperations: An operation that would result in ICR < MCR is not permitted"
        );
    }

    function _requireICRisNotBelowCCR(uint256 _newICR) internal pure {
        require(_newICR >= CCR, "BorrowerOperations: Operation must leave cdp with ICR >= CCR");
    }

    function _requireNoDecreaseOfICR(uint256 _newICR, uint256 _oldICR) internal pure {
        require(
            _newICR >= _oldICR,
            "BorrowerOperations: Cannot decrease your Cdp's ICR in Recovery Mode"
        );
    }

    function _requireNewTCRisNotBelowCCR(uint256 _newTCR) internal pure {
        require(
            _newTCR >= CCR,
            "BorrowerOperations: An operation that would result in TCR < CCR is not permitted"
        );
    }

    function _requireMinDebt(uint256 _debt) internal pure {
        require(_debt >= MIN_CHANGE, "BorrowerOperations: Debt must be above min");
    }

    function _requireAtLeastMinNetStEthBalance(uint256 _stEthBalance) internal pure {
        require(
            _stEthBalance >= MIN_NET_STETH_BALANCE,
            "BorrowerOperations: Cdp's net stEth balance must not fall below minimum"
        );
    }

    function _requireValidDebtRepayment(uint256 _currentDebt, uint256 _debtRepayment) internal pure {
        require(
            _debtRepayment <= _currentDebt,
            "BorrowerOperations: Amount repaid must not be larger than the Cdp's debt"
        );
    }

    function _requireSufficientEbtcTokenBalance(
        address _account,
        uint256 _debtRepayment
    ) internal view {
        require(
            ebtcToken.balanceOf(_account) >= _debtRepayment,
            "BorrowerOperations: Caller doesnt have enough eBTC to make repayment"
        );
    }

    function _requireBorrowerOrPositionManagerAndUpdateManagerApproval(address _borrower) internal {
        if (_borrower == msg.sender) {
            return; // Early return, no delegation
        }

        PositionManagerApproval _approval = _getPositionManagerApproval(_borrower, msg.sender);
        // Must be an approved position manager at this point
        require(
            _approval != PositionManagerApproval.None,
            "BorrowerOperations: Only borrower account or approved position manager can OpenCdp on borrower's behalf"
        );

        // Conditional Adjustment
        /// @dev If this is a position manager operation with a one-time approval, clear that approval
        /// @dev If the PositionManagerApproval was none, we should have failed with the check in _requireBorrowerOrPositionManagerAndUpdateManagerApproval
        if (_approval == PositionManagerApproval.OneTime) {
            _setPositionManagerApproval(_borrower, msg.sender, PositionManagerApproval.None);
        }
    }

    // --- ICR and TCR getters ---

    // Compute the new collateral ratio, considering the change in coll and debt. Assumes 0 pending rewards.
    function _getNewNominalICRFromCdpChange(
        AdjustCdpLocals memory vars,
        bool _isDebtIncrease
    ) internal pure returns (uint256) {
        (uint256 newCollShares, uint256 newDebt) = _getNewCdpAmounts(
            vars.collShares,
            vars.debt,
            vars.collSharesChange,
            vars.isCollIncrease,
            vars.netDebtChange,
            _isDebtIncrease
        );

        uint256 newNICR = EbtcMath._computeNominalCR(newCollShares, newDebt);
        return newNICR;
    }

    // Compute the new collateral ratio, considering the change in coll and debt. Assumes 0 pending rewards.
    function _getNewICRFromCdpChange(
        uint256 _collShares,
        uint256 _debt,
        uint256 _collSharesChange,
        bool _isCollIncrease,
        uint256 _debtChange,
        bool _isDebtIncrease,
        uint256 _price
    ) internal view returns (uint256) {
        (uint256 newCollShares, uint256 newDebt) = _getNewCdpAmounts(
            _collShares,
            _debt,
            _collSharesChange,
            _isCollIncrease,
            _debtChange,
            _isDebtIncrease
        );

        uint256 newICR = EbtcMath._computeCR(
            collateral.getPooledEthByShares(newCollShares),
            newDebt,
            _price
        );
        return newICR;
    }

    function _getNewCdpAmounts(
        uint256 _collShares,
        uint256 _debt,
        uint256 _collSharesChange,
        bool _isCollIncrease,
        uint256 _debtChange,
        bool _isDebtIncrease
    ) internal pure returns (uint256, uint256) {
        uint256 newCollShares = _collShares;
        uint256 newDebt = _debt;

        newCollShares = _isCollIncrease
            ? _collShares + _collSharesChange
            : _collShares - _collSharesChange;
        newDebt = _isDebtIncrease ? _debt + _debtChange : _debt - _debtChange;

        return (newCollShares, newDebt);
    }

    function _getNewTCRFromCdpChange(
        uint256 _stEthBalanceChange,
        bool _isCollIncrease,
        uint256 _debtChange,
        bool _isDebtIncrease,
        uint256 _price
    ) internal view returns (uint256) {
        uint256 _systemCollShares = getSystemCollShares();
        uint256 systemStEthBalance = collateral.getPooledEthByShares(_systemCollShares);
        uint256 systemDebt = _getSystemDebt();

        systemStEthBalance = _isCollIncrease
            ? systemStEthBalance + _stEthBalanceChange
            : systemStEthBalance - _stEthBalanceChange;
        systemDebt = _isDebtIncrease ? systemDebt + _debtChange : systemDebt - _debtChange;

        uint256 newTCR = EbtcMath._computeCR(systemStEthBalance, systemDebt, _price);
        return newTCR;
    }

    // === Flash Loans === //

    /// @notice Borrow assets with a flash loan
    /// @param receiver The address to receive the flash loan
    /// @param token The address of the token to loan
    /// @param amount The amount of tokens to loan
    /// @param data Additional data
    /// @return A boolean value indicating whether the operation was successful
    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) external override returns (bool) {
        require(amount > 0, "BorrowerOperations: 0 Amount");
        uint256 fee = flashFee(token, amount); // NOTE: Check for `eBTCToken` is implicit here // NOTE: Pause check is here
        require(amount <= maxFlashLoan(token), "BorrowerOperations: Too much");

        // Issue EBTC
        ebtcToken.mint(address(receiver), amount);

        // Callback
        require(
            receiver.onFlashLoan(msg.sender, token, amount, fee, data) == FLASH_SUCCESS_VALUE,
            "IERC3156: Callback failed"
        );

        // Gas: Repay from user balance, so we don't trigger a new SSTORE
        // Safe to use transferFrom and unchecked as it's a standard token
        // Also saves gas
        // Send both fee and amount to FEE_RECIPIENT, to burn allowance per EIP-3156
        ebtcToken.transferFrom(address(receiver), feeRecipientAddress, fee + amount);

        // Burn amount, from FEE_RECIPIENT
        ebtcToken.burn(feeRecipientAddress, amount);

        emit FlashLoanSuccess(address(receiver), token, amount, fee);

        return true;
    }

    /// @notice Calculate the flash loan fee for a given token and amount loaned
    /// @param token The address of the token to calculate the fee for
    /// @param amount The amount of tokens to calculate the fee for
    /// @return The flashloan fee calcualted for given token and loan amount
    function flashFee(address token, uint256 amount) public view override returns (uint256) {
        require(token == address(ebtcToken), "BorrowerOperations: EBTC Only");
        require(!flashLoansPaused, "BorrowerOperations: Flash Loans Paused");

        return (amount * feeBps) / MAX_BPS;
    }

    /// @notice Get the maximum flash loan amount for a specific token
    /// @param token The address of the token to get the maximum flash loan amount for, exclusively used here for eBTC token
    /// @return The maximum available flashloan amount for the token, equals to `type(uint112).max`
    function maxFlashLoan(address token) public view override returns (uint256) {
        if (token != address(ebtcToken)) {
            return 0;
        }

        if (flashLoansPaused) {
            return 0;
        }

        return type(uint112).max;
    }

    // === Governed Functions ==

    /// @notice Sets new Fee for FlashLoans
    /// @param _newFee The new flashloan fee to be set
    function setFeeBps(uint256 _newFee) external requiresAuth {
        require(_newFee <= MAX_FEE_BPS, "ERC3156FlashLender: _newFee should <= MAX_FEE_BPS");

        cdpManager.syncGlobalAccounting();

        // set new flash fee
        uint256 _oldFee = feeBps;
        feeBps = uint16(_newFee);
        emit FlashFeeSet(msg.sender, _oldFee, _newFee);
    }

    /// @notice Should Flashloans be paused?
    /// @param _paused The flag (true or false) whether flashloan will be paused
    function setFlashLoansPaused(bool _paused) external requiresAuth {
        cdpManager.syncGlobalAccounting();

        flashLoansPaused = _paused;
        emit FlashLoansPaused(msg.sender, _paused);
    }
}

File 2 of 33 : AuthNoOwner.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

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

/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Modified by BadgerDAO to remove owner
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
contract AuthNoOwner {
    event AuthorityUpdated(address indexed user, Authority indexed newAuthority);

    Authority private _authority;
    bool private _authorityInitialized;

    modifier requiresAuth() virtual {
        require(isAuthorized(msg.sender, msg.sig), "Auth: UNAUTHORIZED");

        _;
    }

    function authority() public view returns (Authority) {
        return _authority;
    }

    function authorityInitialized() public view returns (bool) {
        return _authorityInitialized;
    }

    function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
        Authority auth = _authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.

        // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
        // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
        return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig));
    }

    /// @notice Changed constructor to initialize to allow flexiblity of constructor vs initializer use
    /// @notice sets authorityInitiailzed flag to ensure only one use of
    function _initializeAuthority(address newAuthority) internal {
        require(address(_authority) == address(0), "Auth: authority is non-zero");
        require(!_authorityInitialized, "Auth: authority already initialized");

        _authority = Authority(newAuthority);
        _authorityInitialized = true;

        emit AuthorityUpdated(address(this), Authority(newAuthority));
    }
}

File 3 of 33 : Authority.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
    function canCall(address user, address target, bytes4 functionSig) external view returns (bool);
}

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

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

File 5 of 33 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity 0.8.17;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 6 of 33 : EbtcBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

import "./BaseMath.sol";
import "./EbtcMath.sol";
import "../Interfaces/IActivePool.sol";
import "../Interfaces/IPriceFeed.sol";
import "../Interfaces/IEbtcBase.sol";
import "../Dependencies/ICollateralToken.sol";

/*
 * Base contract for CdpManager, BorrowerOperations. Contains global system constants and
 * common functions.
 */
contract EbtcBase is BaseMath, IEbtcBase {
    // Collateral Ratio applied for Liquidation Incentive
    // i.e., liquidator repay $1 worth of debt to get back $1.03 worth of collateral
    uint256 public constant LICR = 1030000000000000000; // 103%

    // Minimum collateral ratio for individual cdps
    uint256 public constant MCR = 1100000000000000000; // 110%

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

    // Amount of stETH collateral to be locked in active pool on opening cdps
    uint256 public constant LIQUIDATOR_REWARD = 2e17;

    // Minimum amount of stETH collateral a CDP must have
    uint256 public constant MIN_NET_STETH_BALANCE = 2e18;

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

    uint256 public constant BORROWING_FEE_FLOOR = 0; // 0.5%

    uint256 public constant STAKING_REWARD_SPLIT = 5_000; // taking 50% cut from staking reward

    uint256 public constant MAX_REWARD_SPLIT = 10_000;

    uint256 public constant MIN_CHANGE = 1000;

    IActivePool public immutable activePool;

    IPriceFeed public immutable override priceFeed;

    // the only collateral token allowed in CDP
    ICollateralToken public immutable collateral;

    /// @notice Initializes the contract with the provided addresses
    /// @param _activePoolAddress The address of the ActivePool contract
    /// @param _priceFeedAddress The address of the PriceFeed contract
    /// @param _collateralAddress The address of the CollateralToken contract
    constructor(address _activePoolAddress, address _priceFeedAddress, address _collateralAddress) {
        activePool = IActivePool(_activePoolAddress);
        priceFeed = IPriceFeed(_priceFeedAddress);
        collateral = ICollateralToken(_collateralAddress);
    }

    // --- Gas compensation functions ---

    function _calcNetStEthBalance(uint256 _stEthBalance) internal pure returns (uint256) {
        return _stEthBalance - LIQUIDATOR_REWARD;
    }

    /// @notice Get the entire system collateral
    /// @notice Entire system collateral = collateral allocated to system in ActivePool, using it's internal accounting
    /// @dev Collateral tokens stored in ActivePool for liquidator rewards, fees, or coll in CollSurplusPool, are not included
    function getSystemCollShares() public view returns (uint256 entireSystemColl) {
        return (activePool.getSystemCollShares());
    }

    /**
        @notice Get the entire system debt
        @notice Entire system collateral = collateral stored in ActivePool, using their internal accounting
     */
    function _getSystemDebt() internal view returns (uint256 entireSystemDebt) {
        return (activePool.getSystemDebt());
    }

    function _getCachedTCR(uint256 _price) internal view returns (uint256 TCR) {
        (TCR, , ) = _getTCRWithSystemDebtAndCollShares(_price);
    }

    function _getTCRWithSystemDebtAndCollShares(
        uint256 _price
    ) internal view returns (uint256 TCR, uint256 _coll, uint256 _debt) {
        uint256 systemCollShares = getSystemCollShares();
        uint256 systemDebt = _getSystemDebt();

        uint256 _systemStEthBalance = collateral.getPooledEthByShares(systemCollShares);
        TCR = EbtcMath._computeCR(_systemStEthBalance, systemDebt, _price);

        return (TCR, systemCollShares, systemDebt);
    }

    function _checkRecoveryMode(uint256 _price) internal view returns (bool) {
        return _checkRecoveryModeForTCR(_getCachedTCR(_price));
    }

    function _checkRecoveryModeForTCR(uint256 _tcr) internal view returns (bool) {
        return _tcr < CCR;
    }

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

    // Convert debt denominated in ETH to debt denominated in BTC given that _price is ETH/BTC
    // _debt is denominated in ETH
    // _price is ETH/BTC
    function _convertDebtDenominationToBtc(
        uint256 _debt,
        uint256 _price
    ) internal pure returns (uint256) {
        return (_debt * _price) / DECIMAL_PRECISION;
    }

    /// @dev return true if given ICR is qualified for liquidation compared to configured threshold
    /// @dev this function ONLY checks numbers not check grace period switch for Recovery Mode
    function _checkICRAgainstLiqThreshold(uint256 _icr, uint _tcr) internal view returns (bool) {
        // Either undercollateralized
        // OR, it's RM AND they meet the requirement
        // Swapped Requirement && RM to save gas
        return
            _checkICRAgainstMCR(_icr) ||
            (_checkICRAgainstTCR(_icr, _tcr) && _checkRecoveryModeForTCR(_tcr));
    }

    /// @dev return true if given ICR is qualified for liquidation compared to MCR
    function _checkICRAgainstMCR(uint256 _icr) internal view returns (bool) {
        return _icr < MCR;
    }

    /// @dev return true if given ICR is qualified for liquidation compared to TCR
    /// @dev typically used in Recovery Mode
    function _checkICRAgainstTCR(uint256 _icr, uint _tcr) internal view returns (bool) {
        /// @audit is _icr <= _tcr more dangerous for overal system safety?
        /// @audit Should we use _icr < CCR to allow any risky CDP being liquidated?
        return _icr <= _tcr;
    }
}

File 7 of 33 : EbtcMath.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

library EbtcMath {
    uint256 internal constant DECIMAL_PRECISION = 1e18;
    uint256 public constant MAX_TCR = type(uint256).max;

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

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

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

    /**
     * credit to OpenZeppelin
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "EbtcMath: downcast to uint128 will overflow");
        return uint128(value);
    }

    /*
     * 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) CdpManager._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 _collShares, uint256 _debt) internal pure returns (uint256) {
        if (_debt > 0) {
            return (_collShares * NICR_PRECISION) / _debt;
        }
        // Return the maximal value for uint256 if the Cdp has a debt of 0. Represents "infinite" CR.
        else {
            // if (_debt == 0)
            return MAX_TCR;
        }
    }

    /// @dev Compute collateralization ratio, given stETH balance, price, and debt balance
    function _computeCR(
        uint256 _stEthBalance,
        uint256 _debt,
        uint256 _price
    ) internal pure returns (uint256) {
        if (_debt > 0) {
            uint256 newCollRatio = (_stEthBalance * _price) / _debt;

            return newCollRatio;
        }
        // Return the maximal value for uint256 if the Cdp has a debt of 0. Represents "infinite" CR.
        else {
            // if (_debt == 0)
            return MAX_TCR;
        }
    }
}

File 8 of 33 : ERC3156FlashLender.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

import "../Interfaces/IERC3156FlashLender.sol";
import "../Interfaces/IWETH.sol";

abstract contract ERC3156FlashLender is IERC3156FlashLender {
    uint256 public constant MAX_BPS = 10_000;
    uint256 public constant MAX_FEE_BPS = 1_000; // 10%
    bytes32 public constant FLASH_SUCCESS_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");

    // Functions to modify these variables must be included in impelemnting contracts if desired
    uint16 public feeBps = 3; // may be subject to future adjustments through protocol governance
    bool public flashLoansPaused;
}

File 9 of 33 : ICollateralToken.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import "./IERC20.sol";

/**
 * Based on the stETH:
 *  -   https://docs.lido.fi/contracts/lido#
 */
interface ICollateralToken is IERC20 {
    // Returns the amount of shares that corresponds to _ethAmount protocol-controlled Ether
    function getSharesByPooledEth(uint256 _ethAmount) external view returns (uint256);

    // Returns the amount of Ether that corresponds to _sharesAmount token shares
    function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);

    // Moves `_sharesAmount` token shares from the caller's account to the `_recipient` account.
    function transferShares(address _recipient, uint256 _sharesAmount) external returns (uint256);

    // Returns the amount of shares owned by _account
    function sharesOf(address _account) external view returns (uint256);

    // Returns authorized oracle address
    function getOracle() external view returns (address);
}

File 10 of 33 : ICollateralTokenOracle.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

/**
 * Based on the stETH:
 *  -   https://docs.lido.fi/contracts/lido#
 */
interface ICollateralTokenOracle {
    // Return beacon specification data.
    function getBeaconSpec()
        external
        view
        returns (
            uint64 epochsPerFrame,
            uint64 slotsPerEpoch,
            uint64 secondsPerSlot,
            uint64 genesisTime
        );
}

File 11 of 33 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

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

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

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

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

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

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

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

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

    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function decimals() external view returns (uint8);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

File 12 of 33 : IERC2612.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

/**
 * @dev Interface of the ERC2612 standard as defined in the EIP.
 *
 * Adds the {permit} method, which can be used to change one's
 * {IERC20-allowance} without having to send a transaction, by signing a
 * message. This allows users to spend tokens without having to hold Ether.
 *
 * See https://eips.ethereum.org/EIPS/eip-2612.
 *
 * Code adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/
 */
interface IERC2612 {
    /**
     * @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens,
     * given `owner`'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current ERC2612 nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases `owner`'s nonce by one. This
     * prevents a signature from being used multiple times.
     *
     * `owner` can limit the time a Permit is valid for by setting `deadline` to
     * a value in the near future. The deadline argument can be set to uint256(-1) to
     * create Permits that effectively never expire.
     */
    function nonces(address owner) external view returns (uint256);

    function version() external view returns (string memory);

    function permitTypeHash() external view returns (bytes32);

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

File 13 of 33 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity 0.8.17;

import "./Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

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

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

File 14 of 33 : PermitNonce.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;
import "../Interfaces/IPermitNonce.sol";

/**
 * @dev This abstract contract provides a mapping from address to nonce (uint256) used for permit signature
 */
contract PermitNonce is IPermitNonce {
    mapping(address => uint256) internal _nonces;

    /// @dev Increase current nonce for msg.sender by one.
    /// @notice This function could be used to invalidate any signed permit out there
    function increasePermitNonce() external returns (uint256) {
        return ++_nonces[msg.sender];
    }

    /// @dev Return current nonce for msg.sender fOR EIP-2612 compatibility
    function nonces(address owner) external view virtual returns (uint256) {
        return _nonces[owner];
    }
}

File 15 of 33 : ReentrancyGuard.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 internal constant OPEN = 1;
    uint256 internal constant LOCKED = 2;

    uint256 public locked = OPEN;

    modifier nonReentrant() virtual {
        require(locked == OPEN, "ReentrancyGuard: Reentrancy in nonReentrant call");

        locked = LOCKED;

        _;

        locked = OPEN;
    }
}

File 16 of 33 : IActivePool.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

import "./IPool.sol";
import "./ITwapWeightedObserver.sol";

interface IActivePool is IPool, ITwapWeightedObserver {
    // --- Events ---
    event ActivePoolEBTCDebtUpdated(uint256 _EBTCDebt);
    event SystemCollSharesUpdated(uint256 _coll);
    event FeeRecipientClaimableCollSharesIncreased(uint256 _coll, uint256 _fee);
    event FeeRecipientClaimableCollSharesDecreased(uint256 _coll, uint256 _fee);
    event FlashLoanSuccess(
        address indexed _receiver,
        address indexed _token,
        uint256 _amount,
        uint256 _fee
    );
    event SweepTokenSuccess(address indexed _token, uint256 _amount, address indexed _recipient);

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

    function increaseSystemCollShares(uint256 _value) external;

    function transferSystemCollSharesAndLiquidatorReward(
        address _account,
        uint256 _shares,
        uint256 _liquidatorRewardShares
    ) external;

    function allocateSystemCollSharesToFeeRecipient(uint256 _shares) external;

    function claimFeeRecipientCollShares(uint256 _shares) external;

    function feeRecipientAddress() external view returns (address);

    function getFeeRecipientClaimableCollShares() external view returns (uint256);
}

File 17 of 33 : IBaseTwapWeightedObserver.sol
// SPDX-License Identifier: MIT
pragma solidity 0.8.17;

interface IBaseTwapWeightedObserver {
    // NOTE: Packing manually is cheaper, but this is simpler to understand and follow
    struct PackedData {
        // Slot 0
        // Seconds in a year: 3.154e+7
        /// @dev Accumulator value recorded for TWAP Observer until last update
        uint128 observerCumuVal; // 3.154e+7 * 80 * 100e27 = 2.5232e+38 | log_2(100e27 * 3.154e+7 * 80) = 127.568522171
        /// @dev Accumulator for TWAP globally
        uint128 accumulator; // 3.154e+7 * 80 * 100e27 = 2.5232e+38 | log_2(100e27 * 3.154e+7 * 80) = 127.568522171
        // NOTE: We can further compress this slot but we will not be able to use only one (see u72 impl)
        /// So what's the point of making the code more complex?

        // Slot 1
        /// @dev last update timestamp for TWAP Observer
        uint64 lastObserved; // Thousands of Years, if we use relative time we can use u32 | Relative to deploy time (as immutable)
        /// @dev last update timestamp for TWAP global track(spot) value
        uint64 lastAccrued; // Thousands of years
        // Expect eBTC debt to never surpass 100e27, which is 100 BILLION eBTC
        // log_2(100e27) = 96.3359147517 | log_2(100e27 / 1e18) = 36.5412090438
        // We could use a u64
        /// @dev average value since last observe
        uint128 lastObservedAverage;
    }
}

File 18 of 33 : IBorrowerOperations.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;
import "./IPositionManagers.sol";

// Common interface for the Cdp Manager.
interface IBorrowerOperations is IPositionManagers {
    // --- Events ---

    event FeeRecipientAddressChanged(address indexed _feeRecipientAddress);
    event FlashLoanSuccess(
        address indexed _receiver,
        address indexed _token,
        uint256 _amount,
        uint256 _fee
    );

    // --- Functions ---

    function openCdp(
        uint256 _EBTCAmount,
        bytes32 _upperHint,
        bytes32 _lowerHint,
        uint256 _stEthBalance
    ) external returns (bytes32);

    function openCdpFor(
        uint _EBTCAmount,
        bytes32 _upperHint,
        bytes32 _lowerHint,
        uint _collAmount,
        address _borrower
    ) external returns (bytes32);

    function addColl(
        bytes32 _cdpId,
        bytes32 _upperHint,
        bytes32 _lowerHint,
        uint256 _stEthBalanceIncrease
    ) external;

    function withdrawColl(
        bytes32 _cdpId,
        uint256 _stEthBalanceDecrease,
        bytes32 _upperHint,
        bytes32 _lowerHint
    ) external;

    function withdrawDebt(
        bytes32 _cdpId,
        uint256 _amount,
        bytes32 _upperHint,
        bytes32 _lowerHint
    ) external;

    function repayDebt(
        bytes32 _cdpId,
        uint256 _amount,
        bytes32 _upperHint,
        bytes32 _lowerHint
    ) external;

    function closeCdp(bytes32 _cdpId) external;

    function adjustCdp(
        bytes32 _cdpId,
        uint256 _stEthBalanceDecrease,
        uint256 _debtChange,
        bool isDebtIncrease,
        bytes32 _upperHint,
        bytes32 _lowerHint
    ) external;

    function adjustCdpWithColl(
        bytes32 _cdpId,
        uint256 _stEthBalanceDecrease,
        uint256 _debtChange,
        bool isDebtIncrease,
        bytes32 _upperHint,
        bytes32 _lowerHint,
        uint256 _stEthBalanceIncrease
    ) external;

    function claimSurplusCollShares() external;

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

File 19 of 33 : ICdpManager.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

import "./IEbtcBase.sol";
import "./ICdpManagerData.sol";

// Common interface for the Cdp Manager.
interface ICdpManager is IEbtcBase, ICdpManagerData {
    // --- Functions ---
    function liquidate(bytes32 _cdpId) external;

    function partiallyLiquidate(
        bytes32 _cdpId,
        uint256 _partialAmount,
        bytes32 _upperPartialHint,
        bytes32 _lowerPartialHint
    ) external;

    function batchLiquidateCdps(bytes32[] calldata _cdpArray) external;

    function redeemCollateral(
        uint256 _EBTCAmount,
        bytes32 _firstRedemptionHint,
        bytes32 _upperPartialRedemptionHint,
        bytes32 _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintNICR,
        uint256 _maxIterations,
        uint256 _maxFee
    ) external;

    function updateStakeAndTotalStakes(bytes32 _cdpId) external returns (uint256);

    function syncAccounting(bytes32 _cdpId) external;

    function closeCdp(bytes32 _cdpId, address _borrower, uint256 _debt, uint256 _coll) external;

    function getRedemptionRate() external view returns (uint256);

    function getRedemptionRateWithDecay() external view returns (uint256);

    function getRedemptionFeeWithDecay(uint256 _stETHToRedeem) external view returns (uint256);

    function getCdpStatus(bytes32 _cdpId) external view returns (uint256);

    function getCdpStake(bytes32 _cdpId) external view returns (uint256);

    function getCdpDebt(bytes32 _cdpId) external view returns (uint256);

    function getCdpCollShares(bytes32 _cdpId) external view returns (uint256);

    function getCdpLiquidatorRewardShares(bytes32 _cdpId) external view returns (uint);

    function initializeCdp(
        bytes32 _cdpId,
        uint256 _debt,
        uint256 _coll,
        uint256 _liquidatorRewardShares,
        address _borrower
    ) external;

    function updateCdp(
        bytes32 _cdpId,
        address _borrower,
        uint256 _coll,
        uint256 _debt,
        uint256 _newColl,
        uint256 _newDebt
    ) external;

    function getCachedTCR(uint256 _price) external view returns (uint256);

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

File 20 of 33 : ICdpManagerData.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

import "./ICollSurplusPool.sol";
import "./IEBTCToken.sol";
import "./ISortedCdps.sol";
import "./IActivePool.sol";
import "./IRecoveryModeGracePeriod.sol";
import "../Dependencies/ICollateralTokenOracle.sol";

// Common interface for the Cdp Manager.
interface ICdpManagerData is IRecoveryModeGracePeriod {
    // --- Events ---

    event StakingRewardSplitSet(uint256 _stakingRewardSplit);
    event RedemptionFeeFloorSet(uint256 _redemptionFeeFloor);
    event MinuteDecayFactorSet(uint256 _minuteDecayFactor);
    event BetaSet(uint256 _beta);
    event RedemptionsPaused(bool _paused);

    event Liquidation(uint256 _liquidatedDebt, uint256 _liquidatedColl, uint256 _liqReward);
    event Redemption(
        uint256 _debtToRedeemExpected,
        uint256 _debtToRedeemActual,
        uint256 _collSharesSent,
        uint256 _feeCollShares,
        address indexed _redeemer
    );
    event CdpUpdated(
        bytes32 indexed _cdpId,
        address indexed _borrower,
        address indexed _executor,
        uint256 _oldDebt,
        uint256 _oldCollShares,
        uint256 _debt,
        uint256 _collShares,
        uint256 _stake,
        CdpOperation _operation
    );
    event CdpLiquidated(
        bytes32 indexed _cdpId,
        address indexed _borrower,
        uint _debt,
        uint _collShares,
        CdpOperation _operation,
        address indexed _liquidator,
        uint _premiumToLiquidator
    );
    event CdpPartiallyLiquidated(
        bytes32 indexed _cdpId,
        address indexed _borrower,
        uint256 _debt,
        uint256 _collShares,
        CdpOperation operation,
        address indexed _liquidator,
        uint _premiumToLiquidator
    );
    event BaseRateUpdated(uint256 _baseRate);
    event LastRedemptionTimestampUpdated(uint256 _lastFeeOpTime);
    event TotalStakesUpdated(uint256 _newTotalStakes);
    event SystemSnapshotsUpdated(uint256 _totalStakesSnapshot, uint256 _totalCollateralSnapshot);
    event SystemDebtRedistributionIndexUpdated(uint256 _systemDebtRedistributionIndex);
    event CdpDebtRedistributionIndexUpdated(bytes32 _cdpId, uint256 _cdpDebtRedistributionIndex);
    event CdpArrayIndexUpdated(bytes32 _cdpId, uint256 _newIndex);
    event StEthIndexUpdated(uint256 _oldIndex, uint256 _newIndex, uint256 _updTimestamp);
    event CollateralFeePerUnitUpdated(uint256 _oldPerUnit, uint256 _newPerUnit, uint256 _feeTaken);
    event CdpFeeSplitApplied(
        bytes32 _cdpId,
        uint256 _oldPerUnitCdp,
        uint256 _newPerUnitCdp,
        uint256 _collReduced,
        uint256 _collLeft
    );

    enum CdpOperation {
        openCdp,
        closeCdp,
        adjustCdp,
        syncAccounting,
        liquidateInNormalMode,
        liquidateInRecoveryMode,
        redeemCollateral,
        partiallyLiquidate,
        failedPartialRedemption
    }

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

    // Store the necessary data for a cdp
    struct Cdp {
        uint256 debt;
        uint256 coll;
        uint256 stake;
        uint128 liquidatorRewardShares;
        Status status;
    }

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

    struct CdpDebtAndCollShares {
        uint256 debt;
        uint256 collShares;
    }

    struct LiquidationLocals {
        bytes32 cdpId;
        uint256 partialAmount; // used only for partial liquidation, default 0 means full liquidation
        uint256 price;
        uint256 ICR;
        bytes32 upperPartialHint;
        bytes32 lowerPartialHint;
        bool recoveryModeAtStart;
        uint256 TCR;
        uint256 totalSurplusCollShares;
        uint256 totalCollSharesToSend;
        uint256 totalDebtToBurn;
        uint256 totalDebtToRedistribute;
        uint256 totalLiquidatorRewardCollShares;
    }

    struct LiquidationRecoveryModeLocals {
        uint256 entireSystemDebt;
        uint256 entireSystemColl;
        uint256 totalDebtToBurn;
        uint256 totalCollSharesToSend;
        uint256 totalSurplusCollShares;
        bytes32 cdpId;
        uint256 price;
        uint256 ICR;
        uint256 totalDebtToRedistribute;
        uint256 totalLiquidatorRewardCollShares;
    }

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

    struct LocalVariables_LiquidationSequence {
        uint256 i;
        uint256 ICR;
        bytes32 cdpId;
        bool backToNormalMode;
        uint256 entireSystemDebt;
        uint256 entireSystemColl;
        uint256 price;
        uint256 TCR;
    }

    struct SingleRedemptionInputs {
        bytes32 cdpId;
        uint256 maxEBTCamount;
        uint256 price;
        bytes32 upperPartialRedemptionHint;
        bytes32 lowerPartialRedemptionHint;
        uint256 partialRedemptionHintNICR;
    }

    struct LiquidationValues {
        uint256 entireCdpDebt;
        uint256 debtToBurn;
        uint256 totalCollToSendToLiquidator;
        uint256 debtToRedistribute;
        uint256 collSurplus;
        uint256 liquidatorCollSharesReward;
    }

    struct LiquidationTotals {
        uint256 totalDebtInSequence;
        uint256 totalDebtToBurn;
        uint256 totalCollToSendToLiquidator;
        uint256 totalDebtToRedistribute;
        uint256 totalCollSurplus;
        uint256 totalCollReward;
    }

    // --- Variable container structs for redemptions ---

    struct RedemptionTotals {
        uint256 remainingDebtToRedeem;
        uint256 debtToRedeem;
        uint256 collSharesDrawn;
        uint256 totalCollSharesSurplus;
        uint256 feeCollShares;
        uint256 collSharesToRedeemer;
        uint256 decayedBaseRate;
        uint256 price;
        uint256 systemDebtAtStart;
        uint256 twapSystemDebtAtStart;
        uint256 systemCollSharesAtStart;
        uint256 tcrAtStart;
    }

    struct SingleRedemptionValues {
        uint256 debtToRedeem;
        uint256 collSharesDrawn;
        uint256 collSurplus;
        uint256 liquidatorRewardShares;
        bool cancelledPartial;
        bool fullRedemption;
        uint256 newPartialNICR;
    }

    function getActiveCdpsCount() external view returns (uint256);

    function totalStakes() external view returns (uint256);

    function ebtcToken() external view returns (IEBTCToken);

    function systemStEthFeePerUnitIndex() external view returns (uint256);

    function systemStEthFeePerUnitIndexError() external view returns (uint256);

    function stEthIndex() external view returns (uint256);

    function calcFeeUponStakingReward(
        uint256 _newIndex,
        uint256 _prevIndex
    ) external view returns (uint256, uint256, uint256);

    function syncGlobalAccounting() external; // Accrues StEthFeeSplit without influencing Grace Period

    function syncGlobalAccountingAndGracePeriod() external; // Accrues StEthFeeSplit and influences Grace Period

    function getAccumulatedFeeSplitApplied(
        bytes32 _cdpId,
        uint256 _systemStEthFeePerUnitIndex
    ) external view returns (uint256, uint256);

    function getCachedNominalICR(bytes32 _cdpId) external view returns (uint256);

    function getCachedICR(bytes32 _cdpId, uint256 _price) external view returns (uint256);

    function getSyncedCdpDebt(bytes32 _cdpId) external view returns (uint256);

    function getSyncedCdpCollShares(bytes32 _cdpId) external view returns (uint256);

    function getSyncedICR(bytes32 _cdpId, uint256 _price) external view returns (uint256);

    function getSyncedTCR(uint256 _price) external view returns (uint256);

    function getSyncedSystemCollShares() external view returns (uint256);

    function getSyncedNominalICR(bytes32 _cdpId) external view returns (uint256);

    function getPendingRedistributedDebt(bytes32 _cdpId) external view returns (uint256);

    function hasPendingRedistributedDebt(bytes32 _cdpId) external view returns (bool);

    function getSyncedDebtAndCollShares(
        bytes32 _cdpId
    ) external view returns (uint256 debt, uint256 collShares);

    function canLiquidateRecoveryMode(uint256 icr, uint256 tcr) external view returns (bool);

    function totalCollateralSnapshot() external view returns (uint256);

    function totalStakesSnapshot() external view returns (uint256);
}

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

pragma solidity 0.8.17;

interface ICollSurplusPool {
    // --- Events ---

    event SurplusCollSharesAdded(
        bytes32 indexed _cdpId,
        address indexed _account,
        uint256 _claimableSurplusCollShares,
        uint256 _surplusCollSharesAddedFromCollateral,
        uint256 _surplusCollSharesAddedFromLiquidatorReward
    );
    event CollSharesTransferred(address indexed _to, uint256 _amount);

    event SweepTokenSuccess(address indexed _token, uint256 _amount, address indexed _recipient);

    // --- Contract setters ---

    function getTotalSurplusCollShares() external view returns (uint256);

    function getSurplusCollShares(address _account) external view returns (uint256);

    function increaseSurplusCollShares(
        bytes32 _cdpId,
        address _account,
        uint256 _collateralShares,
        uint256 _liquidatorRewardShares
    ) external;

    function claimSurplusCollShares(address _account) external;

    function increaseTotalSurplusCollShares(uint256 _value) external;
}

File 22 of 33 : IEbtcBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

import "./IPriceFeed.sol";

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

File 23 of 33 : IEBTCToken.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

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

interface IEBTCToken is IERC20, IERC2612 {
    // --- Functions ---

    function mint(address _account, uint256 _amount) external;

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

File 24 of 33 : IERC3156FlashBorrower.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

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 "ERC3156FlashBorrower.onFlashLoan"
     */
    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external returns (bytes32);
}

File 25 of 33 : IERC3156FlashLender.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

import "./IERC3156FlashBorrower.sol";

interface IERC3156FlashLender {
    event FlashFeeSet(address indexed _setter, uint256 _oldFee, uint256 _newFee);
    event MaxFlashFeeSet(address indexed _setter, uint256 _oldMaxFee, uint256 _newMaxFee);
    event FlashLoansPaused(address indexed _setter, bool _paused);

    /// @dev The amount of currency available to be lent.
    /// @param token The loan currency.
    /// @return The amount of `token` that can be borrowed.
    function maxFlashLoan(address token) external view returns (uint256);

    /// @dev The fee to be charged for a given loan.
    /// @param token The loan currency.
    /// @param amount The amount of tokens lent.
    /// @return The amount of `token` to be charged for the loan, on top of the returned principal.
    function flashFee(address token, uint256 amount) external view returns (uint256);

    /// @dev Initiate a flash loan.
    /// @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
    /// @param token The loan currency.
    /// @param amount The amount of tokens lent.
    /// @param data Arbitrary data structure, intended to contain user-defined parameters.
    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) external returns (bool);
}

File 26 of 33 : IPermitNonce.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

interface IPermitNonce {
    // --- Functions ---
    function increasePermitNonce() external returns (uint256);

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

File 27 of 33 : IPool.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

// Common interface for the Pools.
interface IPool {
    // --- Events ---

    event ETHBalanceUpdated(uint256 _newBalance);
    event EBTCBalanceUpdated(uint256 _newBalance);
    event CollSharesTransferred(address indexed _to, uint256 _amount);

    // --- Functions ---

    function getSystemCollShares() external view returns (uint256);

    function getSystemDebt() external view returns (uint256);

    function increaseSystemDebt(uint256 _amount) external;

    function decreaseSystemDebt(uint256 _amount) external;
}

File 28 of 33 : IPositionManagers.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

interface IPositionManagers {
    enum PositionManagerApproval {
        None,
        OneTime,
        Persistent
    }

    event PositionManagerApprovalSet(
        address indexed _borrower,
        address indexed _positionManager,
        PositionManagerApproval _approval
    );

    function getPositionManagerApproval(
        address _borrower,
        address _positionManager
    ) external view returns (PositionManagerApproval);

    function setPositionManagerApproval(
        address _positionManager,
        PositionManagerApproval _approval
    ) external;

    function revokePositionManagerApproval(address _positionManager) external;

    function renouncePositionManagerApproval(address _borrower) external;

    function permitPositionManagerApproval(
        address _borrower,
        address _positionManager,
        PositionManagerApproval _approval,
        uint _deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    function version() external view returns (string memory);

    function permitTypeHash() external view returns (bytes32);

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

File 29 of 33 : IPriceFeed.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

interface IPriceFeed {
    // --- Events ---
    event LastGoodPriceUpdated(uint256 _lastGoodPrice);
    event PriceFeedStatusChanged(Status newStatus);
    event FallbackCallerChanged(
        address indexed _oldFallbackCaller,
        address indexed _newFallbackCaller
    );
    event UnhealthyFallbackCaller(address indexed _fallbackCaller, uint256 timestamp);
    event CollateralFeedSourceUpdated(address indexed stEthFeed);

    // --- Structs ---

    struct ChainlinkResponse {
        uint80 roundEthBtcId;
        uint80 roundStEthEthId;
        uint256 answer;
        uint256 timestampEthBtc;
        uint256 timestampStEthEth;
        bool success;
    }

    struct FallbackResponse {
        uint256 answer;
        uint256 timestamp;
        bool success;
    }

    // --- Enum ---

    enum Status {
        chainlinkWorking,
        usingFallbackChainlinkUntrusted,
        bothOraclesUntrusted,
        usingFallbackChainlinkFrozen,
        usingChainlinkFallbackUntrusted
    }

    // --- Function ---
    function fetchPrice() external returns (uint256);
}

File 30 of 33 : IRecoveryModeGracePeriod.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

// Interface for State Updates that can trigger RM Liquidations
interface IRecoveryModeGracePeriod {
    event TCRNotified(uint256 TCR); /// NOTE: Mostly for debugging to ensure synch

    // NOTE: Ts is implicit in events (it's added by GETH)
    event GracePeriodStart();
    event GracePeriodEnd();
    event GracePeriodDurationSet(uint256 _recoveryModeGracePeriodDuration);

    function notifyStartGracePeriod(uint256 tcr) external;

    function notifyEndGracePeriod(uint256 tcr) external;
}

File 31 of 33 : ISortedCdps.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

// Common interface for the SortedCdps Doubly Linked List.
interface ISortedCdps {
    // --- Events ---

    event NodeAdded(bytes32 _id, uint _NICR);
    event NodeRemoved(bytes32 _id);

    // --- Functions ---

    function remove(bytes32 _id) external;

    function batchRemove(bytes32[] memory _ids) external;

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

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

    function isFull() external view returns (bool);

    function isEmpty() external view returns (bool);

    function getSize() external view returns (uint256);

    function getMaxSize() external view returns (uint256);

    function getFirst() external view returns (bytes32);

    function getLast() external view returns (bytes32);

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

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

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

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

    function insert(
        address owner,
        uint256 _ICR,
        bytes32 _prevId,
        bytes32 _nextId
    ) external returns (bytes32);

    function getOwnerAddress(bytes32 _id) external pure returns (address);

    function nonExistId() external view returns (bytes32);

    function cdpCountOf(address owner) external view returns (uint256);

    function getCdpCountOf(
        address owner,
        bytes32 startNodeId,
        uint maxNodes
    ) external view returns (uint256, bytes32);

    function getCdpsOf(address owner) external view returns (bytes32[] memory);

    function getAllCdpsOf(
        address owner,
        bytes32 startNodeId,
        uint maxNodes
    ) external view returns (bytes32[] memory, uint256, bytes32);

    function cdpOfOwnerByIndex(address owner, uint256 index) external view returns (bytes32);

    function cdpOfOwnerByIdx(
        address owner,
        uint256 index,
        bytes32 startNodeId,
        uint maxNodes
    ) external view returns (bytes32, bool);

    function toCdpId(
        address owner,
        uint256 blockHeight,
        uint256 nonce
    ) external pure returns (bytes32);

    function nextCdpNonce() external view returns (uint256);
}

File 32 of 33 : ITwapWeightedObserver.sol
// SPDX-License Identifier: MIT
pragma solidity 0.8.17;
import {IBaseTwapWeightedObserver} from "./IBaseTwapWeightedObserver.sol";

interface ITwapWeightedObserver is IBaseTwapWeightedObserver {
    event TwapDisabled();

    function PERIOD() external view returns (uint256);

    function valueToTrack() external view returns (uint128);

    function timeToAccrue() external view returns (uint64);

    function getLatestAccumulator() external view returns (uint128);

    function observe() external returns (uint256);

    function update() external;

    function twapDisabled() external view returns (bool);
}

File 33 of 33 : IWETH.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

interface IWETH {
    function deposit() external payable;

    function withdraw(uint256) external;

    function transfer(address to, uint256 amount) external returns (bool);

    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_cdpManagerAddress","type":"address"},{"internalType":"address","name":"_activePoolAddress","type":"address"},{"internalType":"address","name":"_collSurplusPoolAddress","type":"address"},{"internalType":"address","name":"_priceFeedAddress","type":"address"},{"internalType":"address","name":"_sortedCdpsAddress","type":"address"},{"internalType":"address","name":"_ebtcTokenAddress","type":"address"},{"internalType":"address","name":"_feeRecipientAddress","type":"address"},{"internalType":"address","name":"_collTokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_feeRecipientAddress","type":"address"}],"name":"FeeRecipientAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_setter","type":"address"},{"indexed":false,"internalType":"uint256","name":"_oldFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newFee","type":"uint256"}],"name":"FlashFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_receiver","type":"address"},{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"FlashLoanSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_setter","type":"address"},{"indexed":false,"internalType":"bool","name":"_paused","type":"bool"}],"name":"FlashLoansPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_setter","type":"address"},{"indexed":false,"internalType":"uint256","name":"_oldMaxFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newMaxFee","type":"uint256"}],"name":"MaxFlashFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":true,"internalType":"address","name":"_positionManager","type":"address"},{"indexed":false,"internalType":"enum IPositionManagers.PositionManagerApproval","name":"_approval","type":"uint8"}],"name":"PositionManagerApprovalSet","type":"event"},{"inputs":[],"name":"BORROWING_FEE_FLOOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DECIMAL_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FLASH_SUCCESS_VALUE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LICR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQUIDATOR_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE_BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_REWARD_SPLIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MCR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_CHANGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_NET_STETH_BALANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DIVISOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STAKING_REWARD_SPLIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activePool","outputs":[{"internalType":"contract IActivePool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"bytes32","name":"_upperHint","type":"bytes32"},{"internalType":"bytes32","name":"_lowerHint","type":"bytes32"},{"internalType":"uint256","name":"_stEthBalanceIncrease","type":"uint256"}],"name":"addColl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"uint256","name":"_stEthBalanceDecrease","type":"uint256"},{"internalType":"uint256","name":"_debtChange","type":"uint256"},{"internalType":"bool","name":"_isDebtIncrease","type":"bool"},{"internalType":"bytes32","name":"_upperHint","type":"bytes32"},{"internalType":"bytes32","name":"_lowerHint","type":"bytes32"}],"name":"adjustCdp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"uint256","name":"_stEthBalanceDecrease","type":"uint256"},{"internalType":"uint256","name":"_debtChange","type":"uint256"},{"internalType":"bool","name":"_isDebtIncrease","type":"bool"},{"internalType":"bytes32","name":"_upperHint","type":"bytes32"},{"internalType":"bytes32","name":"_lowerHint","type":"bytes32"},{"internalType":"uint256","name":"_stEthBalanceIncrease","type":"uint256"}],"name":"adjustCdpWithColl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authorityInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cdpManager","outputs":[{"internalType":"contract ICdpManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimSurplusCollShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"}],"name":"closeCdp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collSurplusPool","outputs":[{"internalType":"contract ICollSurplusPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract ICollateralToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ebtcToken","outputs":[{"internalType":"contract IEBTCToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeRecipientAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"flashFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC3156FlashBorrower","name":"receiver","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flashLoansPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_positionManager","type":"address"}],"name":"getPositionManagerApproval","outputs":[{"internalType":"enum IPositionManagers.PositionManagerApproval","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSystemCollShares","outputs":[{"internalType":"uint256","name":"entireSystemColl","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"increasePermitNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"locked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxFlashLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"bytes32","name":"_upperHint","type":"bytes32"},{"internalType":"bytes32","name":"_lowerHint","type":"bytes32"},{"internalType":"uint256","name":"_stEthBalance","type":"uint256"}],"name":"openCdp","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"bytes32","name":"_upperHint","type":"bytes32"},{"internalType":"bytes32","name":"_lowerHint","type":"bytes32"},{"internalType":"uint256","name":"_stEthBalance","type":"uint256"},{"internalType":"address","name":"_borrower","type":"address"}],"name":"openCdpFor","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_positionManager","type":"address"},{"internalType":"enum IPositionManagers.PositionManagerApproval","name":"_approval","type":"uint8"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permitPositionManagerApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"permitTypeHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"positionManagers","outputs":[{"internalType":"enum IPositionManagers.PositionManagerApproval","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeed","outputs":[{"internalType":"contract IPriceFeed","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"renouncePositionManagerApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"bytes32","name":"_upperHint","type":"bytes32"},{"internalType":"bytes32","name":"_lowerHint","type":"bytes32"}],"name":"repayDebt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_positionManager","type":"address"}],"name":"revokePositionManagerApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newFee","type":"uint256"}],"name":"setFeeBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setFlashLoansPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_positionManager","type":"address"},{"internalType":"enum IPositionManagers.PositionManagerApproval","name":"_approval","type":"uint8"}],"name":"setPositionManagerApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sortedCdps","outputs":[{"internalType":"contract ISortedCdps","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"uint256","name":"_stEthBalanceDecrease","type":"uint256"},{"internalType":"bytes32","name":"_upperHint","type":"bytes32"},{"internalType":"bytes32","name":"_lowerHint","type":"bytes32"}],"name":"withdrawColl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_cdpId","type":"bytes32"},{"internalType":"uint256","name":"_debt","type":"uint256"},{"internalType":"bytes32","name":"_upperHint","type":"bytes32"},{"internalType":"bytes32","name":"_lowerHint","type":"bytes32"}],"name":"withdrawDebt","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103785760003560e01c80639d938e85116101d3578063d9901b9411610104578063eb7ecac4116100a2578063f698da251161007c578063f698da2514610850578063f92d343314610858578063fd967f47146106c1578063fe5e68fd1461086057600080fd5b8063eb7ecac414610803578063ee007c5814610816578063f1bab2ec1461082957600080fd5b8063e342c72c116100de578063e342c72c146107d1578063e3448035146107e4578063e3f68388146107ec578063e8256651146107f457600080fd5b8063d9901b941461077b578063d9d98ce4146107ab578063dce7fe1a146107be57600080fd5b8063bf7e214f11610171578063cda775f91161014b578063cda775f91461074b578063cf30901214610772578063d55be8c61461077b578063d8dfeb451461078457600080fd5b8063bf7e214f146106f9578063c526a6b014610711578063ca542f891461073857600080fd5b8063a3f4df7e116101ad578063a3f4df7e14610690578063a9beb765146106c1578063ba7be950146106ca578063bb038e15146106d257600080fd5b80639d938e85146106475780639f03866b1461065a578063a20baee61461068157600080fd5b80636ee4c8cf116102ad578063811f6d721161024b5780639a2bde2e116102255780639a2bde2e146106055780639b503bae146106185780639b5dfcbb1461062b5780639c77f4cf1461063e57600080fd5b8063811f6d72146105cd578063970c297f146105e057806397bc1e1b146105f357600080fd5b8063794e572411610287578063794e57241461055f5780637ecebe001461056e5780637f3020c1146105975780637f7dde4a146105a657600080fd5b80636ee4c8cf1461051657806372c27b6214610525578063741bef1a1461053857600080fd5b80634870dd9a1161031a5780635733d58f116102f45780635733d58f146104be5780635cffe9de146104cd578063613255ab146104f0578063636a0de31461050357600080fd5b80634870dd9a1461047d578063511e0bb51461048557806354fd4d501461049857600080fd5b806314e67a031161035657806314e67a03146104065780631b71be2a1461041957806324a9d853146104545780633644e5151461047557600080fd5b8063013380e71461037d5780630e29f101146103c157806310ce43bd146103d6575b600080fd5b6103a47f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb81565b6040516001600160a01b0390911681526020015b60405180910390f35b6103d46103cf3660046148ff565b610873565b005b7fa3e89b8382a1bf9cfbf89ac68306e7843d0a6cded34c8c26f6ea9d4a2c23ac095b6040519081526020016103b8565b6103d4610414366004614952565b610964565b610447610427366004614980565b600360209081526000928352604080842090915290825290205460ff1681565b6040516103b891906149f1565b6001546104629061ffff1681565b60405161ffff90911681526020016103b8565b6103f8610f36565b6103f860c881565b6103f86104933660046149ff565b610f45565b6040805180820190915260018152603160f81b60208201525b6040516103b89190614a31565b6103f8671158e460913d000081565b6104e06104db366004614a7f565b61102a565b60405190151581526020016103b8565b6103f86104fe366004614b1e565b611416565b6103d46105113660046149ff565b611487565b6103f8671bc16d674ec8000081565b6103d4610533366004614952565b61156f565b6103a47f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a81565b6103f8670f43fc2c04ee000081565b6103f861057c366004614b1e565b6001600160a01b031660009081526002602052604090205490565b6103f86702c68af0bb14000081565b6103a47f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc81565b6103d46105db3660046149ff565b611705565b6103d46105ee366004614b3b565b6117e1565b600154600160b81b900460ff166104e0565b6103d46106133660046149ff565b611903565b6001546104e09062010000900460ff1681565b6103d4610639366004614b1e565b6119e0565b6103f861138881565b6103d4610655366004614b58565b6119ef565b6103f87f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd981565b6103f8670de0b6b3a764000081565b6104b160405180604001604052806012815260200171426f72726f7765724f7065726174696f6e7360701b81525081565b6103f861271081565b6103d4611ad4565b6103a47f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c37571177481565b600154630100000090046001600160a01b03166103a4565b6103a47f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be381565b6103d4610746366004614bc8565b611b4f565b6103a47f000000000000000000000000335982dae827049d35f09d5ec927de2bc38df3de81565b6103f860005481565b6103f86103e881565b6103a47f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8481565b6103f86107b9366004614c46565b611d7b565b6104476107cc366004614980565b611e8e565b6103f86107df366004614c72565b611ebe565b6103f8611fa4565b6103f8611fca565b6103f8670e4b4b8af6a7000081565b6103d46108113660046149ff565b61204e565b6103d4610824366004614b1e565b61212a565b6103a47f0000000000000000000000002ceb95d4a67bf771f1165659df3d11d8871e906f81565b6103f8612136565b6103f8600081565b6103d461086e366004614cbf565b6121ef565b60016000541461089e5760405162461bcd60e51b815260040161089590614cf4565b60405180910390fd5b60017f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109229190614d47565b1461093f5760405162461bcd60e51b815260040161089590614d60565b600260008190555061095786868686868660006121fe565b5050600160005550505050565b604051626a1f6360e11b8152600481018290526000907f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b03169062d43ec690602401602060405180830381865afa1580156109ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ee9190614dab565b90506109f981612a33565b610a237f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c37571177483612b4e565b6040516344da1c8f60e11b8152600481018390527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b0316906389b4391e90602401600060405180830381600087803b158015610a8557600080fd5b505af1158015610a99573d6000803e3d6000fd5b5050505060007f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a6001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610aff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b239190614d47565b9050610b36610b3182612c2d565b612c41565b60405163299f69db60e11b8152600481018490526000907f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b03169063533ed3b690602401602060405180830381865afa158015610b9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc29190614d47565b604051633b24f8af60e21b8152600481018690529091506000906001600160a01b037f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c375711774169063ec93e2bc90602401602060405180830381865afa158015610c2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c519190614d47565b6040516318db92df60e11b8152600481018790529091506000906001600160a01b037f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c37571177416906331b725be90602401602060405180830381865afa158015610cbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce09190614d47565b9050610cec3383612cc7565b604051630f451f7160e31b815260048101849052600090610d87906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa158015610d58573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7c9190614d47565b600085600089612dd7565b9050610d9281612eda565b6040516394691d8560e01b8152600481018290527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b0316906394691d8590602401600060405180830381600087803b158015610df457600080fd5b505af1158015610e08573d6000803e3d6000fd5b505060405163be6b86af60e01b8152600481018a90526001600160a01b03898116602483015260448201879052606482018890527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c37571177416925063be6b86af9150608401600060405180830381600087803b158015610e8457600080fd5b505af1158015610e98573d6000803e3d6000fd5b50505050610ea63384612f71565b604051634667d4f360e11b815233600482015260248101859052604481018390527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031690638ccfa9e690606401600060405180830381600087803b158015610f1557600080fd5b505af1158015610f29573d6000803e3d6000fd5b5050505050505050505050565b6000610f40612136565b905090565b6000600160005414610f695760405162461bcd60e51b815260040161089590614cf4565b60017f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fed9190614d47565b1461100a5760405162461bcd60e51b815260040161089590614d60565b600260005561101c8585858533613072565b600160005595945050505050565b600080841161107b5760405162461bcd60e51b815260206004820152601c60248201527f426f72726f7765724f7065726174696f6e733a203020416d6f756e74000000006044820152606401610895565b60006110878686611d7b565b905061109286611416565b8511156110e15760405162461bcd60e51b815260206004820152601c60248201527f426f72726f7765724f7065726174696f6e733a20546f6f206d756368000000006044820152606401610895565b6040516340c10f1960e01b81526001600160a01b038881166004830152602482018790527f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb16906340c10f1990604401600060405180830381600087803b15801561114b57600080fd5b505af115801561115f573d6000803e3d6000fd5b50506040516323e30c8b60e01b81527f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd992506001600160a01b038a1691506323e30c8b906111bb9033908b908b9088908c908c90600401614dc8565b6020604051808303816000875af11580156111da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111fe9190614d47565b1461124b5760405162461bcd60e51b815260206004820152601960248201527f49455243333135363a2043616c6c6261636b206661696c6564000000000000006044820152606401610895565b6001600160a01b037f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb166323b872dd887f0000000000000000000000002ceb95d4a67bf771f1165659df3d11d8871e906f6112a68986614e3a565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303816000875af11580156112fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131e9190614e4d565b50604051632770a7eb60e21b81526001600160a01b037f0000000000000000000000002ceb95d4a67bf771f1165659df3d11d8871e906f81166004830152602482018790527f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb1690639dc29fac90604401600060405180830381600087803b1580156113a957600080fd5b505af11580156113bd573d6000803e3d6000fd5b505060408051888152602081018590526001600160a01b03808b1694508b1692507fbc26d11fd70e2cdca20b501a51cf33594c9f434ee2c77fe035ef21b0cf14000b910160405180910390a35060019695505050505050565b60007f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb6001600160a01b0316826001600160a01b03161461145957506000919050565b60015462010000900460ff161561147257506000919050565b506dffffffffffffffffffffffffffff919050565b6001600054146114a95760405162461bcd60e51b815260040161089590614cf4565b60017f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015611509573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061152d9190614d47565b1461154a5760405162461bcd60e51b815260040161089590614d60565b6002600081905550611564846000856001868660006121fe565b505060016000555050565b611585336000356001600160e01b03191661374e565b6115c65760405162461bcd60e51b8152602060048201526012602482015271105d5d1a0e8815539055551213d49256915160721b6044820152606401610895565b6103e88111156116325760405162461bcd60e51b815260206004820152603160248201527f45524333313536466c6173684c656e6465723a205f6e65774665652073686f756044820152706c64203c3d204d41585f4645455f42505360781b6064820152608401610895565b7f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663adf6cd166040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561168d57600080fd5b505af11580156116a1573d6000803e3d6000fd5b50506001805461ffff85811661ffff19831617909255604051911692503391507f36d567e1fd131473c440012eaa0903f9a646c89a1ee00ecc01c477d63d94d287906116f99084908690918252602082015260400190565b60405180910390a25050565b6001600054146117275760405162461bcd60e51b815260040161089590614cf4565b60017f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015611787573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ab9190614d47565b146117c85760405162461bcd60e51b815260040161089590614d60565b60026000819055506115648484600080868660006121fe565b6117f7336000356001600160e01b03191661374e565b6118385760405162461bcd60e51b8152602060048201526012602482015271105d5d1a0e8815539055551213d49256915160721b6044820152606401610895565b7f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663adf6cd166040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561189357600080fd5b505af11580156118a7573d6000803e3d6000fd5b505060018054841515620100000262ff000019909116179055505060405133907fa5b0db65e66479c7d3871018efb8f6b08c5bcd03b257b342d53b13c91396c3df906118f890841515815260200190565b60405180910390a250565b6001600054146119255760405162461bcd60e51b815260040161089590614cf4565b60017f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015611985573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a99190614d47565b146119c65760405162461bcd60e51b815260040161089590614d60565b6002600081905550611564846000856000868660006121fe565b6119ec813360006137f6565b50565b600160005414611a115760405162461bcd60e51b815260040161089590614cf4565b60017f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a959190614d47565b14611ab25760405162461bcd60e51b815260040161089590614d60565b6002600055611ac6878787878787876121fe565b505060016000555050505050565b60405163ede3c6f760e01b81523360048201527f000000000000000000000000335982dae827049d35f09d5ec927de2bc38df3de6001600160a01b03169063ede3c6f790602401600060405180830381600087803b158015611b3557600080fd5b505af1158015611b49573d6000803e3d6000fd5b50505050565b42841015611bbb5760405162461bcd60e51b815260206004820152603360248201527f426f72726f7765724f7065726174696f6e733a20506f736974696f6e206d616e6044820152721859d95c881c195c9b5a5d08195e1c1a5c9959606a1b6064820152608401610895565b6000611bc5612136565b6001600160a01b038916600090815260026020526040812080547fa3e89b8382a1bf9cfbf89ac68306e7843d0a6cded34c8c26f6ea9d4a2c23ac09928c928c928c92909190611c1383614e6a565b919050558a604051602001611c2d96959493929190614e83565b60405160208183030381529060405280519060200120604051602001611c6a92919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015611cd5573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590611d0b5750886001600160a01b0316816001600160a01b0316145b611d655760405162461bcd60e51b815260206004820152602560248201527f426f72726f7765724f7065726174696f6e733a20496e76616c6964207369676e604482015264617475726560d81b6064820152608401610895565b611d708989896137f6565b505050505050505050565b60007f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb6001600160a01b0316836001600160a01b031614611dfe5760405162461bcd60e51b815260206004820152601d60248201527f426f72726f7765724f7065726174696f6e733a2045425443204f6e6c790000006044820152606401610895565b60015462010000900460ff1615611e665760405162461bcd60e51b815260206004820152602660248201527f426f72726f7765724f7065726174696f6e733a20466c617368204c6f616e732060448201526514185d5cd95960d21b6064820152608401610895565b60015461271090611e7b9061ffff1684614ec2565b611e859190614ed9565b90505b92915050565b6001600160a01b03808316600090815260036020908152604080832093851683529290529081205460ff16611e85565b6000600160005414611ee25760405162461bcd60e51b815260040161089590614cf4565b60017f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f669190614d47565b14611f835760405162461bcd60e51b815260040161089590614d60565b6002600055611f958686868686613072565b60016000559695505050505050565b33600090815260026020526040812080548290611fc090614e6a565b9182905550919050565b60007f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663e3f683886040518163ffffffff1660e01b8152600401602060405180830381865afa15801561202a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f409190614d47565b6001600054146120705760405162461bcd60e51b815260040161089590614cf4565b60017f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f49190614d47565b146121115760405162461bcd60e51b815260040161089590614d60565b60026000819055506115648460008060008787876121fe565b6119ec338260006137f6565b60007f0000000000000000000000000000000000000000000000000000000000000001460361218457507f4a03fed71ff372205ed2baa32f3c53e3a66a4a4b9e487864b544e28145e3a4d690565b610f407f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f9b33564149118ec9119fbdb8514a1b623225b1ccef2620151741cdd5ed627e2a7fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc661388d565b6121fa3383836137f6565b5050565b604051626a1f6360e11b8152600481018890526000907f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b03169062d43ec690602401602060405180830381865afa158015612264573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122889190614dab565b905061229381612a33565b6122bd7f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c37571177489612b4e565b6040516344da1c8f60e11b8152600481018990527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b0316906389b4391e90602401600060405180830381600087803b15801561231f57600080fd5b505af1158015612333573d6000803e3d6000fd5b5050505061239d60405180610180016040528060008152602001600081526020016000815260200160001515815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b7f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a6001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156123fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124219190614d47565b8152851561243757612432876138d8565b612440565b61244087613950565b61244a83896139e1565b612455838989613a6f565b61245e83613950565b61246788613950565b6124718389613b09565b151560608301526020820152604080820188905251633b24f8af60e21b8152600481018a90527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b03169063ec93e2bc90602401602060405180830381865afa1580156124e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061250c9190614d47565b608082015260405163299f69db60e11b8152600481018a90527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b03169063533ed3b690602401602060405180830381865afa158015612576573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259a9190614d47565b60a08201819052604051630f451f7160e31b81526000916001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841691637a28fb88916125f29160040190815260200190565b602060405180830381865afa15801561260f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126339190614d47565b9050808911156126c25760405162461bcd60e51b815260206004820152604e60248201527f426f72726f7765724f7065726174696f6e733a2043616e6e6f7420776974686460448201527f726177206772656174657220737445746842616c616e6365207468616e20746860648201526d0652076616c756520696e204364760941b608482015260a401610895565b6126d58183608001518460000151613c39565b8260c00181815250506127058260a0015183608001518460200151856060015186604001518c8860000151613c6a565b60e083015281516000906127299061271c90612c2d565b671158e460913d00001190565b9050612737818b8a86613d1c565b871580156127455750600089115b156127865761275c83608001518460400151613f93565b61276a338460400151612cc7565b612786836040015184608001516127819190614efb565b61401a565b6127a88360a0015184608001518560200151866060015187604001518d61407f565b61012085018190526101408501919091526127c29061401a565b610140830151604051630f451f7160e31b8152612860916001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841691637a28fb889161281a9160040190815260200190565b602060405180830381865afa158015612837573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285b9190614d47565b6140d3565b7f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b03166317acaf788c868660a0015187608001518861014001518961012001516040518763ffffffff1660e01b81526004016128f4969594939291909586526001600160a01b0394909416602086015260408501929092526060840152608083015260a082015260c00190565b600060405180830381600087803b15801561290e57600080fd5b505af1158015612922573d6000803e3d6000fd5b505050506000612932848a614161565b604051637b30119360e01b8152600481018e905260248101829052604481018a9052606481018990529091507f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be36001600160a01b031690637b30119390608401600060405180830381600087803b1580156129ac57600080fd5b505af11580156129c0573d6000803e3d6000fd5b505050505060006040518060c00160405280336001600160a01b031681526020018560200151815260200185606001516129fb5760006129fd565b875b81526020018560600151151581526020018b81526020018a15158152509050612a25816141a2565b505050505050505050505050565b336001600160a01b03821603612a465750565b6001600160a01b038116600090815260036020908152604080832033845290915281205460ff1690816002811115612a8057612a806149b9565b03612b295760405162461bcd60e51b815260206004820152606760248201527f426f72726f7765724f7065726174696f6e733a204f6e6c7920626f72726f776560448201527f72206163636f756e74206f7220617070726f76656420706f736974696f6e206d60648201527f616e616765722063616e204f70656e436470206f6e20626f72726f7765722773608482015266103132b430b63360c91b60a482015260c401610895565b6001816002811115612b3d57612b3d6149b9565b036121fa576121fa823360006137f6565b6040516301b7d96360e01b8152600481018290526000906001600160a01b038416906301b7d96390602401602060405180830381865afa158015612b96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bba9190614d47565b905080600114612c285760405162461bcd60e51b815260206004820152603360248201527f426f72726f7765724f7065726174696f6e733a2043647020646f6573206e6f7460448201527208195e1a5cdd081bdc881a5cc818db1bdcd959606a1b6064820152608401610895565b505050565b6000612c388261428b565b50909392505050565b612c5281671158e460913d00001190565b156119ec576040805162461bcd60e51b81526020600482015260248101919091527f426f72726f7765724f7065726174696f6e733a204f7065726174696f6e206e6f60448201527f74207065726d697474656420647572696e67205265636f76657279204d6f64656064820152608401610895565b6040516370a0823160e01b81526001600160a01b03838116600483015282917f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb909116906370a0823190602401602060405180830381865afa158015612d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d559190614d47565b10156121fa5760405162461bcd60e51b8152602060048201526044602482018190527f426f72726f7765724f7065726174696f6e733a2043616c6c657220646f65736e908201527f74206861766520656e6f756768206542544320746f206d616b652072657061796064820152631b595b9d60e21b608482015260a401610895565b600080612de2611fca565b604051630f451f7160e31b8152600481018290529091506000906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa158015612e4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e719190614d47565b90506000612e7d61434f565b905087612e9357612e8e8983614efb565b612e9d565b612e9d8983614e3a565b915085612eb357612eae8782614efb565b612ebd565b612ebd8782614e3a565b90506000612ecc838388613c39565b9a9950505050505050505050565b671158e460913d00008110156119ec5760405162461bcd60e51b815260206004820152605060248201527f426f72726f7765724f7065726174696f6e733a20416e206f7065726174696f6e60448201527f207468617420776f756c6420726573756c7420696e20544352203c204343522060648201526f1a5cc81b9bdd081c195c9b5a5d1d195960821b608482015260a401610895565b6040516349f047bb60e01b8152600481018290527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b0316906349f047bb90602401600060405180830381600087803b158015612fd357600080fd5b505af1158015612fe7573d6000803e3d6000fd5b5050604051632770a7eb60e21b81526001600160a01b038581166004830152602482018590527f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb169250639dc29fac91506044015b600060405180830381600087803b15801561305657600080fd5b505af115801561306a573d6000803e3d6000fd5b505050505050565b600061307d8661401a565b61308682612a33565b6130bf6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6130c8846143af565b604082018190526130d8906140d3565b7f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b031663adf6cd166040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561313357600080fd5b505af1158015613147573d6000803e3d6000fd5b505050507f000000000000000000000000a9a65b1b1dda8376527e89985b221b6bfca1dc9a6001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156131ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131cf9190614d47565b81526020810187905260408101516132445760405162461bcd60e51b815260206004820152603260248201527f426f72726f7765724f7065726174696f6e733a207a65726f20636f6c6c61746560448201527172616c20666f72206f70656e43647028292160701b6064820152608401610895565b6040808201519051631920845160e01b81526000916001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84169163192084519161329a9160040190815260200190565b602060405180830381865afa1580156132b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132db9190614d47565b604051631920845160e01b81526702c68af0bb14000060048201529091506000906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690631920845190602401602060405180830381865afa15801561334d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133719190614d47565b905061338a836040015184602001518560000151613c39565b6060840152602083015161339f9083906143c3565b608084015282516000906133b69061271c90612c2d565b905060006133d585604001516001876020015160018960000151612dd7565b905081156134c7576133ea85606001516143f9565b671158e460913d000081101561347a5760405163028dba8960e01b8152600481018290527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b03169063028dba89906024015b600060405180830381600087803b15801561345d57600080fd5b505af1158015613471573d6000803e3d6000fd5b50505050613558565b6040516394691d8560e01b8152600481018290527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b0316906394691d8590602401613443565b6134d48560600151614477565b6134dd81612eda565b6040516394691d8560e01b8152600481018290527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b0316906394691d8590602401600060405180830381600087803b15801561353f57600080fd5b505af1158015613553573d6000803e3d6000fd5b505050505b60808501516040516301c4b66960e41b81526001600160a01b0389811660048301526024820192909252604481018c9052606481018b90526000917f000000000000000000000000591acb5ae192c147948c12651a0a5f24f0529be31690631c4b6690906084016020604051808303816000875af11580156135de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136029190614d47565b905061360d8161450e565b6020860151604051634494bf4d60e01b815260048101839052602481019190915260448101869052606481018590526001600160a01b0389811660848301527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117741690634494bf4d9060a401600060405180830381600087803b15801561369257600080fd5b505af11580156136a6573d6000803e3d6000fd5b505050506136b4338d614610565b6136be89866146df565b886702c68af0bb14000087604001516136d79190614e3a565b1461373f5760405162461bcd60e51b815260206004820152603260248201527f426f72726f7765724f7065726174696f6e733a206465706f736974656420636f6044820152716c6c61746572616c206d69736d617463682160701b6064820152608401610895565b9b9a5050505050505050505050565b600154600090630100000090046001600160a01b031680158015906137ee575060405163b700961360e01b81526001600160a01b0385811660048301523060248301526001600160e01b03198516604483015282169063b700961390606401602060405180830381865afa1580156137ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137ee9190614e4d565b949350505050565b6001600160a01b038084166000908152600360209081526040808320938616835292905220805482919060ff19166001836002811115613838576138386149b9565b0217905550816001600160a01b0316836001600160a01b03167f8ad811f5960ce068d3890d69410b5b420e0ed5e2424a94a8895f319c8aa053338360405161388091906149f1565b60405180910390a3505050565b600083838346604080516020810195909552840192909252606083015260808201523060a082015260c0016040516020818303038152906040528051906020012090505b9392505050565b6103e88110156119ec5760405162461bcd60e51b815260206004820152603960248201527f426f72726f7765724f7065726174696f6e733a204465627420696e637265617360448201527f65207265717569726573206d696e20646562744368616e6765000000000000006064820152608401610895565b80158061395f57506103e88110155b6119ec5760405162461bcd60e51b815260206004820152604760248201527f426f72726f7765724f7065726174696f6e733a20436f6c6c61746572616c206f60448201527f722064656274206368616e6765206d757374206265207a65726f206f7220616260648201526637bb329036b4b760c91b608482015260a401610895565b8115806139ec575080155b6121fa5760405162461bcd60e51b815260206004820152604860248201527f426f72726f7765724f7065726174696f6e733a2043616e6e6f7420616464206160448201527f6e6420776974686472617720636f6c6c61746572616c20696e2073616d65206f6064820152673832b930ba34b7b760c11b608482015260a401610895565b6000831180613a7e5750600081115b80613a895750600082115b612c285760405162461bcd60e51b8152602060048201526044602482018190527f426f72726f7765724f7065726174696f6e733a205468657265206d7573742062908201527f6520656974686572206120636f6c6c61746572616c206f722064656274206368606482015263616e676560e01b608482015260a401610895565b6000808315613ba657604051631920845160e01b8152600481018590527f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b031690631920845190602401602060405180830381865afa158015613b77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b9b9190614d47565b915060019050613c32565b604051631920845160e01b8152600481018490527f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b031690631920845190602401602060405180830381865afa158015613c0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c2f9190614d47565b91505b9250929050565b60008215613c6157600083613c4e8487614ec2565b613c589190614ed9565b91506138d19050565b506000196138d1565b6000806000613c7d8a8a8a8a8a8a61407f565b91509150600061373f7f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b0316637a28fb88856040518263ffffffff1660e01b8152600401613cd491815260200190565b602060405180830381865afa158015613cf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d159190614d47565b8387613c39565b6020810151604051630f451f7160e31b8152613dc7916001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841691637a28fb8891613d739160040190815260200190565b602060405180830381865afa158015613d90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613db49190614d47565b6060830151604084015184518690612dd7565b6101008201528315613ef157613ddc836147e6565b8115613e0157613def8160e001516143f9565b613e018160e001518260c0015161486f565b671158e460913d00008161010001511015613e9d5761010081015160405163028dba8960e01b815260048101919091527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b03169063028dba89906024015b600060405180830381600087803b158015613e8057600080fd5b505af1158015613e94573d6000803e3d6000fd5b50505050611b49565b6101008101516040516394691d8560e01b815260048101919091527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b0316906394691d8590602401613e66565b613efe8160e00151614477565b613f0c816101000151612eda565b6101008101516040516394691d8560e01b815260048101919091527f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b0316906394691d8590602401600060405180830381600087803b158015613f7557600080fd5b505af1158015613f89573d6000803e3d6000fd5b5050505050505050565b818111156121fa5760405162461bcd60e51b815260206004820152604860248201527f426f72726f7765724f7065726174696f6e733a20416d6f756e7420726570616960448201527f64206d757374206e6f74206265206c6172676572207468616e207468652043646064820152671c09dcc81919589d60c21b608482015260a401610895565b6103e88110156119ec5760405162461bcd60e51b815260206004820152602a60248201527f426f72726f7765724f7065726174696f6e733a2044656274206d7573742062656044820152691030b137bb329036b4b760b11b6064820152608401610895565b60008087878661409857614093888b614efb565b6140a2565b6140a2888b614e3a565b9150846140b8576140b3868a614efb565b6140c2565b6140c2868a614e3a565b919a91995090975050505050505050565b671bc16d674ec800008110156119ec5760405162461bcd60e51b815260206004820152604760248201527f426f72726f7765724f7065726174696f6e733a204364702773206e657420737460448201527f4574682062616c616e6365206d757374206e6f742066616c6c2062656c6f77206064820152666d696e696d756d60c81b608482015260a401610895565b60008060006141888560a0015186608001518760200151886060015189604001518961407f565b91509150600061419883836143c3565b9695505050505050565b8060a00151156141c3576141be81600001518260800151614610565b6141d5565b6141d581600001518260800151612f71565b8060600151156141f1576119ec816040015182602001516146df565b805160208201516040516338339d3160e11b81526001600160a01b037f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc16926370673a6292614256926004016001600160a01b03929092168252602082015260400190565b600060405180830381600087803b15801561427057600080fd5b505af1158015614284573d6000803e3d6000fd5b5050505050565b600080600080614299611fca565b905060006142a561434f565b604051630f451f7160e31b8152600481018490529091506000906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe841690637a28fb8890602401602060405180830381865afa158015614310573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143349190614d47565b9050614341818389613c39565b979296509094509092505050565b60007f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b031663facc05116040518163ffffffff1660e01b8152600401602060405180830381865afa15801561202a573d6000803e3d6000fd5b6000611e886702c68af0bb14000083614efb565b600081156143f057816143df68056bc75e2d6310000085614ec2565b6143e99190614ed9565b9050611e88565b50600019611e88565b671158e460913d00008110156119ec5760405162461bcd60e51b815260206004820152603c60248201527f426f72726f7765724f7065726174696f6e733a204f7065726174696f6e206d7560448201527f7374206c6561766520636470207769746820494352203e3d20434352000000006064820152608401610895565b670f43fc2c04ee00008110156119ec5760405162461bcd60e51b815260206004820152605060248201527f426f72726f7765724f7065726174696f6e733a20416e206f7065726174696f6e60448201527f207468617420776f756c6420726573756c7420696e20494352203c204d43522060648201526f1a5cc81b9bdd081c195c9b5a5d1d195960821b608482015260a401610895565b6040516301b7d96360e01b8152600481018290526000907f000000000000000000000000c4cbae499bb4ca41e78f52f07f5d98c3757117746001600160a01b0316906301b7d96390602401602060405180830381865afa158015614576573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061459a9190614d47565b905080156121fa5760405162461bcd60e51b815260206004820152603f60248201527f426f72726f7765724f7065726174696f6e733a2043647020697320616374697660448201527f65206f7220686173206265656e2070726576696f75736c7920636c6f736564006064820152608401610895565b604051635f5c6aef60e11b8152600481018290527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b03169063beb8d5de90602401600060405180830381600087803b15801561467257600080fd5b505af1158015614686573d6000803e3d6000fd5b50506040516340c10f1960e01b81526001600160a01b038581166004830152602482018590527f000000000000000000000000661c70333aa1850ccdbae82776bb436a0fcfeefb1692506340c10f19915060440161303c565b6040516323b872dd60e01b81523360048201526001600160a01b037f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc81166024830152604482018490527f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8416906323b872dd906064016020604051808303816000875af1158015614774573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147989190614e4d565b5060405163b13285ff60e01b8152600481018290527f0000000000000000000000006dbdb6d420c110290431e863a1a978ae53f69ebc6001600160a01b03169063b13285ff9060240161303c565b80156119ec5760405162461bcd60e51b815260206004820152604c60248201527f426f72726f7765724f7065726174696f6e733a20436f6c6c61746572616c207760448201527f69746864726177616c206e6f74207065726d697474656420647572696e67205260648201526b65636f76657279204d6f646560a01b608482015260a401610895565b808210156121fa5760405162461bcd60e51b815260206004820152604360248201527f426f72726f7765724f7065726174696f6e733a2043616e6e6f7420646563726560448201527f61736520796f75722043647027732049435220696e205265636f76657279204d6064820152626f646560e81b608482015260a401610895565b80151581146119ec57600080fd5b60008060008060008060c0878903121561491857600080fd5b8635955060208701359450604087013593506060870135614938816148f1565b9598949750929560808101359460a0909101359350915050565b60006020828403121561496457600080fd5b5035919050565b6001600160a01b03811681146119ec57600080fd5b6000806040838503121561499357600080fd5b823561499e8161496b565b915060208301356149ae8161496b565b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b600381106149ed57634e487b7160e01b600052602160045260246000fd5b9052565b60208101611e8882846149cf565b60008060008060808587031215614a1557600080fd5b5050823594602084013594506040840135936060013592509050565b600060208083528351808285015260005b81811015614a5e57858101830151858201604001528201614a42565b506000604082860101526040601f19601f8301168501019250505092915050565b600080600080600060808688031215614a9757600080fd5b8535614aa28161496b565b94506020860135614ab28161496b565b935060408601359250606086013567ffffffffffffffff80821115614ad657600080fd5b818801915088601f830112614aea57600080fd5b813581811115614af957600080fd5b896020828501011115614b0b57600080fd5b9699959850939650602001949392505050565b600060208284031215614b3057600080fd5b81356138d18161496b565b600060208284031215614b4d57600080fd5b81356138d1816148f1565b600080600080600080600060e0888a031215614b7357600080fd5b8735965060208801359550604088013594506060880135614b93816148f1565b9699959850939660808101359560a0820135955060c0909101359350915050565b803560038110614bc357600080fd5b919050565b600080600080600080600060e0888a031215614be357600080fd5b8735614bee8161496b565b96506020880135614bfe8161496b565b9550614c0c60408901614bb4565b945060608801359350608088013560ff81168114614c2957600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215614c5957600080fd5b8235614c648161496b565b946020939093013593505050565b600080600080600060a08688031215614c8a57600080fd5b853594506020860135935060408601359250606086013591506080860135614cb18161496b565b809150509295509295909350565b60008060408385031215614cd257600080fd5b8235614cdd8161496b565b9150614ceb60208401614bb4565b90509250929050565b60208082526033908201527f426f72726f7765724f7065726174696f6e733a205265656e7472616e637920696040820152721b881b9bdb9499595b9d1c985b9d0818d85b1b606a1b606082015260800190565b600060208284031215614d5957600080fd5b5051919050565b6020808252602b908201527f4364704d616e616765723a205265656e7472616e637920696e206e6f6e52656560408201526a1b9d1c985b9d0818d85b1b60aa1b606082015260800190565b600060208284031215614dbd57600080fd5b81516138d18161496b565b6001600160a01b03878116825286166020820152604081018590526060810184905260a06080820181905281018290526000828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b634e487b7160e01b600052601160045260246000fd5b80820180821115611e8857611e88614e24565b600060208284031215614e5f57600080fd5b81516138d1816148f1565b600060018201614e7c57614e7c614e24565b5060010190565b8681526001600160a01b0386811660208301528516604082015260c08101614eae60608301866149cf565b608082019390935260a00152949350505050565b8082028115828204841417611e8857611e88614e24565b600082614ef657634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115611e8857611e88614e2456fea264697066735822122016517250608696dfb70e43a24314f0f7ec67e7018cd61bc314fdbe50e5ee41c764736f6c63430008110033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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