ETH Price: $3,893.46 (+4.72%)
Gas: 16.5 Gwei

Contract

0xE6343ad0675C9b8D3f32679ae6aDbA0766A2ab4c
 

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Extend Loan213329912024-12-05 1:23:357 hrs ago1733361815IN
0xE6343ad0...766A2ab4c
0 ETH0.0036121724.0163516
Extend Loan213251582024-12-03 23:08:3533 hrs ago1733267315IN
0xE6343ad0...766A2ab4c
0 ETH0.0030493322.87490052
Extend Loan213251502024-12-03 23:06:5933 hrs ago1733267219IN
0xE6343ad0...766A2ab4c
0 ETH0.0034146625.61542724
Extend Loan213251462024-12-03 23:06:1133 hrs ago1733267171IN
0xE6343ad0...766A2ab4c
0 ETH0.0039883426.51740198
Extend Loan213210832024-12-03 9:28:2346 hrs ago1733218103IN
0xE6343ad0...766A2ab4c
0 ETH0.0024611819
Extend Loan213210792024-12-03 9:27:3547 hrs ago1733218055IN
0xE6343ad0...766A2ab4c
0 ETH0.0025209717.19062431
Extend Loan213145932024-12-02 11:41:352 days ago1733139695IN
0xE6343ad0...766A2ab4c
0 ETH0.0016280812.5673915
Extend Loan213145882024-12-02 11:40:352 days ago1733139635IN
0xE6343ad0...766A2ab4c
0 ETH0.0017344513.38853629
Extend Loan213132762024-12-02 7:16:353 days ago1733123795IN
0xE6343ad0...766A2ab4c
0 ETH0.0015505511.63162338
Extend Loan213132742024-12-02 7:16:113 days ago1733123771IN
0xE6343ad0...766A2ab4c
0 ETH0.0015395711.55030309
Extend Loan213128142024-12-02 5:43:353 days ago1733118215IN
0xE6343ad0...766A2ab4c
0 ETH0.001756511.67946257
Extend Loan213119022024-12-02 2:39:593 days ago1733107199IN
0xE6343ad0...766A2ab4c
0 ETH0.002015515.11946936
Extend Loan213118972024-12-02 2:38:593 days ago1733107139IN
0xE6343ad0...766A2ab4c
0 ETH0.002123115.92667768
Extend Loan213118942024-12-02 2:38:233 days ago1733107103IN
0xE6343ad0...766A2ab4c
0 ETH0.0021044515.78673373
Extend Loan213118892024-12-02 2:37:233 days ago1733107043IN
0xE6343ad0...766A2ab4c
0 ETH0.0019383114.54047402
Extend Loan213118842024-12-02 2:36:233 days ago1733106983IN
0xE6343ad0...766A2ab4c
0 ETH0.0021196415.90073175
Extend Loan213102672024-12-01 21:11:473 days ago1733087507IN
0xE6343ad0...766A2ab4c
0 ETH0.0027386421.13998821
Extend Loan213102632024-12-01 21:10:593 days ago1733087459IN
0xE6343ad0...766A2ab4c
0 ETH0.0027897321.53437474
Extend Loan213101372024-12-01 20:45:473 days ago1733085947IN
0xE6343ad0...766A2ab4c
0 ETH0.0030646920.9
Extend Loan213048512024-12-01 3:03:594 days ago1733022239IN
0xE6343ad0...766A2ab4c
0 ETH0.000921199.25898846
Extend Loan213048182024-12-01 2:57:234 days ago1733021843IN
0xE6343ad0...766A2ab4c
0 ETH0.001177049.68681892
Extend Loan213048112024-12-01 2:55:594 days ago1733021759IN
0xE6343ad0...766A2ab4c
0 ETH0.001110039.13532697
Extend Loan213047612024-12-01 2:45:594 days ago1733021159IN
0xE6343ad0...766A2ab4c
0 ETH0.0015757512.9681004
Lend To Cooler213024802024-11-30 19:07:594 days ago1732993679IN
0xE6343ad0...766A2ab4c
0 ETH0.0094387113.52761512
Lend To Cooler212990772024-11-30 7:43:115 days ago1732952591IN
0xE6343ad0...766A2ab4c
0 ETH0.004710036.63042911
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Clearinghouse

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 10 runs

Other Settings:
london EvmVersion
File 1 of 17 : Clearinghouse.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {ERC4626} from "solmate/mixins/ERC4626.sol";
import {IStaking} from "interfaces/IStaking.sol";

import {CoolerFactory, Cooler} from "cooler/CoolerFactory.sol";
import {CoolerCallback} from "cooler/CoolerCallback.sol";

import {ROLESv1, RolesConsumer} from "modules/ROLES/OlympusRoles.sol";
import {TRSRYv1} from "modules/TRSRY/TRSRY.v1.sol";
import {MINTRv1} from "modules/MINTR/MINTR.v1.sol";
import "src/Kernel.sol";

/// @title  Olympus Clearinghouse.
/// @notice Olympus Clearinghouse (Policy) Contract.
/// @dev    The Olympus Clearinghouse is a lending facility built on top of Cooler Loans. The Clearinghouse
///         ensures that OHM holders can take loans against their gOHM holdings according to the parameters
///         approved by the community in OIP-144 and its subsequent RFCs. The Clearinghouse parameters are
///         immutable, because of that, if backing was to increase substantially, a new governance process
///         to fork this implementation with upgraded parameters should take place.
///         Although the Cooler contracts allow lenders to transfer ownership of their repayment rights, the
///         Clearinghouse doesn't implement any functions to use that feature.
contract Clearinghouse is Policy, RolesConsumer, CoolerCallback {
    // --- ERRORS ----------------------------------------------------

    error BadEscrow();
    error DurationMaximum();
    error OnlyBurnable();
    error TooEarlyToFund();
    error LengthDiscrepancy();
    error OnlyBorrower();
    error NotLender();

    // --- EVENTS ----------------------------------------------------

    /// @notice Logs whenever the Clearinghouse is deactivated.
    event Deactivate();
    /// @notice Logs whenever the Clearinghouse is reactivated.
    event Reactivate();
    /// @notice Logs whenever the treasury is defunded.
    event Defund(address token, uint256 amount);
    /// @notice Logs the balance change (in DAI terms) whenever a rebalance occurs.
    event Rebalance(bool defund, uint256 daiAmount);

    // --- RELEVANT CONTRACTS ----------------------------------------

    ERC20 public immutable dai; // Debt token
    ERC4626 public immutable sdai; // Idle DAI will be wrapped into sDAI
    ERC20 public immutable gohm; // Collateral token
    ERC20 public immutable ohm; // Unwrapped gOHM
    IStaking public immutable staking; // Necessary to unstake (and burn) OHM from defaults

    // --- MODULES ---------------------------------------------------

    TRSRYv1 public TRSRY; // Olympus V3 Treasury Module
    MINTRv1 public MINTR; // Olympus V3 Minter Module

    // --- PARAMETER BOUNDS ------------------------------------------

    uint256 public constant INTEREST_RATE = 5e15; // 0.5% anually
    uint256 public constant LOAN_TO_COLLATERAL = 289292e16; // 2,892.92 DAI/gOHM
    uint256 public constant DURATION = 121 days; // Four months
    uint256 public constant FUND_CADENCE = 7 days; // One week
    uint256 public constant FUND_AMOUNT = 18_000_000e18; // 18 million
    uint256 public constant MAX_REWARD = 1e17; // 0.1 gOHM

    // --- STATE VARIABLES -------------------------------------------

    /// @notice determines whether the contract can be funded or not.
    bool public active;

    /// @notice timestamp at which the next rebalance can occur.
    uint256 public fundTime;

    /// @notice Outstanding receivables.
    /// Incremented when a loan is taken or rolled.
    /// Decremented when a loan is repaid or collateral is burned.
    uint256 public interestReceivables;
    uint256 public principalReceivables;

    // --- INITIALIZATION --------------------------------------------

    constructor(
        address ohm_,
        address gohm_,
        address staking_,
        address sdai_,
        address coolerFactory_,
        address kernel_
    ) Policy(Kernel(kernel_)) CoolerCallback(coolerFactory_) {
        // Store the relevant contracts.
        ohm = ERC20(ohm_);
        gohm = ERC20(gohm_);
        staking = IStaking(staking_);
        sdai = ERC4626(sdai_);
        dai = ERC20(sdai.asset());

        // Initialize the contract status and its funding schedule.
        active = true;
        fundTime = block.timestamp;
    }

    /// @notice Default framework setup. Configure dependencies for olympus-v3 modules.
    /// @dev    This function will be called when the `executor` installs the Clearinghouse
    ///         policy in the olympus-v3 `Kernel`.
    function configureDependencies() external override returns (Keycode[] memory dependencies) {
        dependencies = new Keycode[](3);
        dependencies[0] = toKeycode("TRSRY");
        dependencies[1] = toKeycode("MINTR");
        dependencies[2] = toKeycode("ROLES");

        TRSRY = TRSRYv1(getModuleAddress(toKeycode("TRSRY")));
        MINTR = MINTRv1(getModuleAddress(toKeycode("MINTR")));
        ROLES = ROLESv1(getModuleAddress(toKeycode("ROLES")));

        // Approve MINTR for burning OHM (called here so that it is re-approved on updates)
        ohm.approve(address(MINTR), type(uint256).max);
    }

    /// @notice Default framework setup. Request permissions for interacting with olympus-v3 modules.
    /// @dev    This function will be called when the `executor` installs the Clearinghouse
    ///         policy in the olympus-v3 `Kernel`.
    function requestPermissions() external view override returns (Permissions[] memory requests) {
        Keycode TRSRY_KEYCODE = toKeycode("TRSRY");

        requests = new Permissions[](4);
        requests[0] = Permissions(TRSRY_KEYCODE, TRSRY.setDebt.selector);
        requests[1] = Permissions(TRSRY_KEYCODE, TRSRY.increaseWithdrawApproval.selector);
        requests[2] = Permissions(TRSRY_KEYCODE, TRSRY.withdrawReserves.selector);
        requests[3] = Permissions(toKeycode("MINTR"), MINTR.burnOhm.selector);
    }

    // --- OPERATION -------------------------------------------------

    /// @notice Lend to a cooler.
    /// @dev    To simplify the UX and easily ensure that all holders get the same terms,
    ///         this function requests a new loan and clears it in the same transaction.
    /// @param  cooler_ to lend to.
    /// @param  amount_ of DAI to lend.
    /// @return the id of the granted loan.
    function lendToCooler(Cooler cooler_, uint256 amount_) external returns (uint256) {
        // Attempt a Clearinghouse <> Treasury rebalance.
        rebalance();

        // Validate that cooler was deployed by the trusted factory.
        if (!factory.created(address(cooler_))) revert OnlyFromFactory();

        // Validate cooler collateral and debt tokens.
        if (cooler_.collateral() != gohm || cooler_.debt() != dai) revert BadEscrow();

        // Transfer in collateral owed
        uint256 collateral = cooler_.collateralFor(amount_, LOAN_TO_COLLATERAL);
        gohm.transferFrom(msg.sender, address(this), collateral);

        // Increment interest to be expected
        (, uint256 interest) = getLoanForCollateral(collateral);
        interestReceivables += interest;
        principalReceivables += amount_;

        // Create a new loan request.
        gohm.approve(address(cooler_), collateral);
        uint256 reqID = cooler_.requestLoan(amount_, INTEREST_RATE, LOAN_TO_COLLATERAL, DURATION);

        // Clear the created loan request by providing enough DAI.
        sdai.withdraw(amount_, address(this), address(this));
        dai.approve(address(cooler_), amount_);
        uint256 loanID = cooler_.clearRequest(reqID, address(this), true);

        return loanID;
    }

    /// @notice Extend the loan expiry by repaying the extension interest in advance.
    ///         The extension cost is paid by the caller. If a third-party executes the
    ///         extension, the loan period is extended, but the borrower debt does not increase.
    /// @param  cooler_ holding the loan to be extended.
    /// @param  loanID_ index of loan in loans[].
    /// @param  times_ Amount of times that the fixed-term loan duration is extended.
    function extendLoan(Cooler cooler_, uint256 loanID_, uint8 times_) external {
        Cooler.Loan memory loan = cooler_.getLoan(loanID_);

        // Validate that cooler was deployed by the trusted factory.
        if (!factory.created(address(cooler_))) revert OnlyFromFactory();

        // Calculate extension interest based on the remaining principal.
        uint256 interestBase = interestForLoan(loan.principal, loan.request.duration);

        // Transfer in extension interest from the caller.
        dai.transferFrom(msg.sender, address(this), interestBase * times_);
        if (active) {
            _sweepIntoDSR(interestBase * times_);
        } else {
            _defund(dai, interestBase * times_);
        }

        // Signal to cooler that loan should be extended.
        cooler_.extendLoanTerms(loanID_, times_);
    }

    /// @notice Batch several default claims to save gas.
    ///         The elements on both arrays must be paired based on their index.
    /// @dev    Implements an auction style reward system that linearly increases up to a max reward.
    /// @param  coolers_ Array of contracts where the default must be claimed.
    /// @param  loans_ Array of defaulted loan ids.
    function claimDefaulted(address[] calldata coolers_, uint256[] calldata loans_) external {
        uint256 loans = loans_.length;
        if (loans != coolers_.length) revert LengthDiscrepancy();

        uint256 totalPrincipal;
        uint256 totalInterest;
        uint256 totalCollateral;
        uint256 keeperRewards;
        for (uint256 i = 0; i < loans; ) {
            // Validate that cooler was deployed by the trusted factory.
            if (!factory.created(coolers_[i])) revert OnlyFromFactory();

            // Validate that loan was written by clearinghouse.
            if (Cooler(coolers_[i]).getLoan(loans_[i]).lender != address(this)) revert NotLender();

            // Claim defaults and update cached metrics.
            (uint256 principal, uint256 interest, uint256 collateral, uint256 elapsed) = Cooler(
                coolers_[i]
            ).claimDefaulted(loans_[i]);

            unchecked {
                // Cannot overflow due to max supply limits for both tokens
                totalPrincipal += principal;
                totalInterest += interest;
                totalCollateral += collateral;
                // There will not exist more than 2**256 loans
                ++i;
            }

            // Cap rewards to 5% of the collateral to avoid OHM holder's dillution.
            uint256 maxAuctionReward = (collateral * 5e16) / 1e18;

            // Cap rewards to avoid exorbitant amounts.
            uint256 maxReward = (maxAuctionReward < MAX_REWARD) ? maxAuctionReward : MAX_REWARD;

            // Calculate rewards based on the elapsed time since default.
            keeperRewards = (elapsed < 7 days)
                ? keeperRewards + (maxReward * elapsed) / 7 days
                : keeperRewards + maxReward;
        }

        // Decrement loan receivables.
        interestReceivables = (interestReceivables > totalInterest)
            ? interestReceivables - totalInterest
            : 0;
        principalReceivables = (principalReceivables > totalPrincipal)
            ? principalReceivables - totalPrincipal
            : 0;

        // Update outstanding debt owed to the Treasury upon default.
        uint256 outstandingDebt = TRSRY.reserveDebt(dai, address(this));

        // debt owed to TRSRY = user debt - user interest
        TRSRY.setDebt({
            debtor_: address(this),
            token_: dai,
            amount_: (outstandingDebt > totalPrincipal) ? outstandingDebt - totalPrincipal : 0
        });

        // Reward keeper.
        gohm.transfer(msg.sender, keeperRewards);
        // Burn the outstanding collateral of defaulted loans.
        burn();
    }

    // --- CALLBACKS -----------------------------------------------------

    /// @notice Overridden callback to decrement loan receivables.
    /// @param *unused loadID_ of the load.
    /// @param  principalPaid_ in DAI.
    /// @param  interestPaid_ in DAI.
    function _onRepay(uint256, uint256 principalPaid_, uint256 interestPaid_) internal override {
        if (active) {
            _sweepIntoDSR(principalPaid_ + interestPaid_);
        } else {
            _defund(dai, principalPaid_ + interestPaid_);
        }

        // Decrement loan receivables.
        interestReceivables = (interestReceivables > interestPaid_)
            ? interestReceivables - interestPaid_
            : 0;
        principalReceivables = (principalReceivables > principalPaid_)
            ? principalReceivables - principalPaid_
            : 0;
    }

    /// @notice Unused callback since defaults are handled by the clearinghouse.
    /// @dev    Overriden and left empty to save gas.
    function _onDefault(uint256, uint256, uint256, uint256) internal override {}

    // --- FUNDING ---------------------------------------------------

    /// @notice Fund loan liquidity from treasury.
    /// @dev    Exposure is always capped at FUND_AMOUNT and rebalanced at up to FUND_CADANCE.
    ///         If several rebalances are available (because some were missed), calling this
    ///         function several times won't impact the funds controlled by the contract.
    ///         If the emergency shutdown is triggered, a rebalance will send funds back to
    ///         the treasury.
    /// @return False if too early to rebalance. Otherwise, true.
    function rebalance() public returns (bool) {
        // If the contract is deactivated, defund.
        uint256 maxFundAmount = active ? FUND_AMOUNT : 0;
        // Update funding schedule if necessary.
        if (fundTime > block.timestamp) return false;
        fundTime += FUND_CADENCE;

        // Sweep DAI into DSR if necessary.
        uint256 idle = dai.balanceOf(address(this));
        if (idle != 0) _sweepIntoDSR(idle);

        uint256 daiBalance = sdai.maxWithdraw(address(this));
        uint256 outstandingDebt = TRSRY.reserveDebt(dai, address(this));
        // Rebalance funds on hand with treasury's reserves.
        if (daiBalance < maxFundAmount) {
            // Since users loans are denominated in DAI, the clearinghouse
            // debt is set in DAI terms. It must be adjusted when funding.
            uint256 fundAmount = maxFundAmount - daiBalance;
            TRSRY.setDebt({
                debtor_: address(this),
                token_: dai,
                amount_: outstandingDebt + fundAmount
            });

            // Since TRSRY holds sDAI, a conversion must be done before
            // funding the clearinghouse.
            uint256 sdaiAmount = sdai.previewWithdraw(fundAmount);
            TRSRY.increaseWithdrawApproval(address(this), sdai, sdaiAmount);
            TRSRY.withdrawReserves(address(this), sdai, sdaiAmount);

            // Log the event.
            emit Rebalance(false, fundAmount);
        } else if (daiBalance > maxFundAmount) {
            // Since users loans are denominated in DAI, the clearinghouse
            // debt is set in DAI terms. It must be adjusted when defunding.
            uint256 defundAmount = daiBalance - maxFundAmount;
            TRSRY.setDebt({
                debtor_: address(this),
                token_: dai,
                amount_: (outstandingDebt > defundAmount) ? outstandingDebt - defundAmount : 0
            });

            // Since TRSRY holds sDAI, a conversion must be done before
            // sending sDAI back.
            uint256 sdaiAmount = sdai.previewWithdraw(defundAmount);
            sdai.transfer(address(TRSRY), sdaiAmount);

            // Log the event.
            emit Rebalance(true, defundAmount);
        }

        return true;
    }

    /// @notice Sweep excess DAI into vault.
    function sweepIntoDSR() public {
        uint256 daiBalance = dai.balanceOf(address(this));
        _sweepIntoDSR(daiBalance);
    }

    /// @notice Sweep excess DAI into vault.
    function _sweepIntoDSR(uint256 amount_) internal {
        dai.approve(address(sdai), amount_);
        sdai.deposit(amount_, address(this));
    }

    /// @notice Return funds to treasury.
    /// @param  token_ to transfer.
    /// @param  amount_ to transfer.
    function defund(ERC20 token_, uint256 amount_) external onlyRole("cooler_overseer") {
        if (token_ == gohm) revert OnlyBurnable();
        _defund(token_, amount_);
    }

    /// @notice Internal function to return funds to treasury.
    /// @param  token_ to transfer.
    /// @param  amount_ to transfer.
    function _defund(ERC20 token_, uint256 amount_) internal {
        if (token_ == sdai || token_ == dai) {
            // Since users loans are denominated in DAI, the clearinghouse
            // debt is set in DAI terms. It must be adjusted when defunding.
            uint256 outstandingDebt = TRSRY.reserveDebt(dai, address(this));
            uint256 daiAmount = (token_ == sdai) ? sdai.previewRedeem(amount_) : amount_;

            TRSRY.setDebt({
                debtor_: address(this),
                token_: dai,
                amount_: (outstandingDebt > daiAmount) ? outstandingDebt - daiAmount : 0
            });
        }

        // Defund and log the event
        token_.transfer(address(TRSRY), amount_);
        emit Defund(address(token_), amount_);
    }

    /// @notice Public function to burn gOHM.
    /// @dev    Can be used to burn any gOHM defaulted using the Cooler instead of the Clearinghouse.
    function burn() public {
        uint256 gohmBalance = gohm.balanceOf(address(this));
        // Unstake and burn gOHM holdings.
        gohm.approve(address(staking), gohmBalance);
        MINTR.burnOhm(address(this), staking.unstake(address(this), gohmBalance, false, false));
    }

    /// @notice Deactivate the contract and return funds to treasury.
    function emergencyShutdown() external onlyRole("emergency_shutdown") {
        active = false;

        // If necessary, defund sDAI.
        uint256 sdaiBalance = sdai.balanceOf(address(this));
        if (sdaiBalance != 0) _defund(sdai, sdaiBalance);

        // If necessary, defund DAI.
        uint256 daiBalance = dai.balanceOf(address(this));
        if (daiBalance != 0) _defund(dai, daiBalance);

        emit Deactivate();
    }

    /// @notice Reactivate the contract.
    function reactivate() external onlyRole("cooler_overseer") {
        active = true;
        fundTime = block.timestamp;

        emit Reactivate();
    }

    // --- AUX FUNCTIONS ---------------------------------------------

    /// @notice view function computing collateral for a loan amount.
    function getCollateralForLoan(uint256 principal_) external pure returns (uint256) {
        return (principal_ * 1e18) / LOAN_TO_COLLATERAL;
    }

    /// @notice view function computing loan for a collateral amount.
    /// @param  collateral_ amount of gOHM.
    /// @return debt (amount to be lent + interest) for a given collateral amount.
    function getLoanForCollateral(uint256 collateral_) public pure returns (uint256, uint256) {
        uint256 principal = (collateral_ * LOAN_TO_COLLATERAL) / 1e18;
        uint256 interest = interestForLoan(principal, DURATION);
        return (principal, interest);
    }

    /// @notice view function to compute the interest for given principal amount.
    /// @param principal_ amount of DAI being lent.
    /// @param duration_ elapsed time in seconds.
    function interestForLoan(uint256 principal_, uint256 duration_) public pure returns (uint256) {
        uint256 interestPercent = (INTEREST_RATE * duration_) / 365 days;
        return (principal_ * interestPercent) / 1e18;
    }

    /// @notice Get total receivable DAI for the treasury.
    ///         Includes both principal and interest.
    function getTotalReceivables() external view returns (uint256) {
        return principalReceivables + interestReceivables;
    }
}

File 2 of 17 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 3 of 17 : ERC4626.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol";

/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)
abstract contract ERC4626 is ERC20 {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    ERC20 public immutable asset;

    constructor(
        ERC20 _asset,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol, _asset.decimals()) {
        asset = _asset;
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual returns (uint256 shares) {
        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual returns (uint256 assets) {
        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        // Check for rounding error since we round down in previewRedeem.
        require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    function totalAssets() public view virtual returns (uint256);

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
    }

    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return convertToShares(assets);
    }

    function previewMint(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return convertToAssets(shares);
    }

    /*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return convertToAssets(balanceOf[owner]);
    }

    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HOOKS LOGIC
    //////////////////////////////////////////////////////////////*/

    function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}

    function afterDeposit(uint256 assets, uint256 shares) internal virtual {}
}

File 4 of 17 : IStaking.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IStaking {
    function unstake(
        address to,
        uint256 amount,
        bool trigger,
        bool rebasing
    ) external returns (uint256);
}

File 5 of 17 : CoolerFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {ClonesWithImmutableArgs} from "clones/ClonesWithImmutableArgs.sol";

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

/// @title  Cooler Loans Factory.
/// @notice The Cooler Factory creates new Cooler escrow contracts.
/// @dev    This contract uses Clones (https://github.com/wighawag/clones-with-immutable-args)
///         to save gas on deployment.
contract CoolerFactory {
    using ClonesWithImmutableArgs for address;

    // --- ERRORS ----------------------------------------------------

    error NotFromFactory();
    error DecimalsNot18();

    // --- EVENTS ----------------------------------------------------

    /// @notice A global event when a new loan request is created.
    event RequestLoan(address indexed cooler, address collateral, address debt, uint256 reqID);
    /// @notice A global event when a loan request is rescinded.
    event RescindRequest(address indexed cooler, uint256 reqID);
    /// @notice A global event when a loan request is fulfilled.
    event ClearRequest(address indexed cooler, uint256 reqID, uint256 loanID);
    /// @notice A global event when a loan is repaid.
    event RepayLoan(address indexed cooler, uint256 loanID, uint256 amount);
    /// @notice A global event when a loan is extended.
    event ExtendLoan(address indexed cooler, uint256 loanID, uint8 times);
    /// @notice A global event when the collateral of defaulted loan is claimed.
    event DefaultLoan(address indexed cooler, uint256 loanID, uint256 amount);

    // -- STATE VARIABLES --------------------------------------------

    /// @notice Cooler reference implementation (deployed on creation to clone from).
    Cooler public immutable coolerImplementation;

    /// @notice Mapping to validate deployed coolers.
    mapping(address => bool) public created;

    /// @notice Mapping to prevent duplicate coolers.
    mapping(address => mapping(ERC20 => mapping(ERC20 => address))) private coolerFor;

    /// @notice Mapping to query Coolers for Collateral-Debt pair.
    mapping(ERC20 => mapping(ERC20 => address[])) public coolersFor;

    // --- INITIALIZATION --------------------------------------------

    constructor() {
        coolerImplementation = new Cooler();
    }

    // --- DEPLOY NEW COOLERS ----------------------------------------

    /// @notice creates a new Escrow contract for collateral and debt tokens.
    /// @param  collateral_ the token given as collateral.
    /// @param  debt_ the token to be lent. Interest is denominated in debt tokens.
    /// @return cooler address of the contract.
function generateCooler(ERC20 collateral_, ERC20 debt_) external returns (address cooler) {
    // Return address if cooler exists.
    cooler = coolerFor[msg.sender][collateral_][debt_];

    // Otherwise generate new cooler.
    if (cooler == address(0)) {
        if (collateral_.decimals() != 18 || debt_.decimals() != 18) revert DecimalsNot18();
        // Clone the cooler implementation.
        bytes memory coolerData = abi.encodePacked(
            msg.sender,              // owner
            address(collateral_),    // collateral
            address(debt_),          // debt
            address(this)            // factory
        );
        cooler = address(coolerImplementation).clone(coolerData);

        // Update storage accordingly.
        coolerFor[msg.sender][collateral_][debt_] = cooler;
        coolersFor[collateral_][debt_].push(cooler);
        created[cooler] = true;
    }
}

    // --- EMIT EVENTS -----------------------------------------------

    /// @notice Ensure that the called is a Cooler.
    modifier onlyFromFactory {        
        if (!created[msg.sender]) revert NotFromFactory();
        _;
    }

    /// @notice Emit a global event when a new loan request is created.
    function logRequestLoan(uint256 reqID_) external onlyFromFactory {
        emit RequestLoan(msg.sender, address(Cooler(msg.sender).collateral()), address(Cooler(msg.sender).debt()), reqID_);
    }

    /// @notice Emit a global event when a loan request is rescinded.
    function logRescindRequest(uint256 reqID_) external onlyFromFactory {
        emit RescindRequest(msg.sender, reqID_);
    }

    /// @notice Emit a global event when a loan request is fulfilled.
    function logClearRequest(uint256 reqID_, uint256 loanID_) external onlyFromFactory {
        emit ClearRequest(msg.sender, reqID_, loanID_);
    }

    /// @notice Emit a global event when a loan is repaid.
    function logRepayLoan(uint256 loanID_, uint256 repayment_) external onlyFromFactory {
        emit RepayLoan(msg.sender, loanID_, repayment_);
    }

    /// @notice Emit a global event when a loan is extended.
    function logExtendLoan(uint256 loanID_, uint8 times_) external onlyFromFactory {
        emit ExtendLoan(msg.sender, loanID_, times_);
    }

    /// @notice Emit a global event when the collateral of defaulted loan is claimed.
    function logDefaultLoan(uint256 loanID_, uint256 collateral_) external onlyFromFactory {
        emit DefaultLoan(msg.sender, loanID_, collateral_);
    }

    // --- AUX FUNCTIONS ---------------------------------------------

    /// @notice Getter function to get an existing cooler for a given user <> collateral <> debt combination.
    function getCoolerFor(address user_, address collateral_, address debt_) public view returns (address) {
        return coolerFor[user_][ERC20(collateral_)][ERC20(debt_)];
    }
}

File 6 of 17 : CoolerCallback.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

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

/// @notice Allows for debt issuers to execute logic when a loan is repaid, rolled, or defaulted.
/// @dev    The three callback functions must be implemented if `isCoolerCallback()` is set to true.
abstract contract CoolerCallback {
    // --- ERRORS ----------------------------------------------------

    error OnlyFromFactory();

    // --- INITIALIZATION --------------------------------------------

    CoolerFactory public immutable factory;

    constructor(address coolerFactory_) {
        factory = CoolerFactory(coolerFactory_);
    }

    // --- EXTERNAL FUNCTIONS ------------------------------------------------

    /// @notice Informs to Cooler that this contract can handle its callbacks.
    function isCoolerCallback() external pure returns (bool) {
        return true;
    }

    /// @notice Callback function that handles repayments.
    function onRepay(uint256 loanID_, uint256 principlePaid_, uint256 interestPaid_) external {
        if (!factory.created(msg.sender)) revert OnlyFromFactory();
        _onRepay(loanID_, principlePaid_, interestPaid_);
    }

    /// @notice Callback function that handles defaults.
    function onDefault(
        uint256 loanID_,
        uint256 principle,
        uint256 interest,
        uint256 collateral
    ) external {
        if (!factory.created(msg.sender)) revert OnlyFromFactory();
        _onDefault(loanID_, principle, interest, collateral);
    }

    // --- INTERNAL FUNCTIONS ------------------------------------------------

    /// @notice Callback function that handles repayments. Override for custom logic.
    function _onRepay(
        uint256 loanID_,
        uint256 principlePaid_,
        uint256 interestPaid_
    ) internal virtual;

    /// @notice Callback function that handles defaults.
    function _onDefault(
        uint256 loanID_,
        uint256 principle_,
        uint256 interestDue_,
        uint256 collateral
    ) internal virtual;
}

File 7 of 17 : OlympusRoles.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.15;

import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol";
import "src/Kernel.sol";

/// @notice Abstract contract to have the `onlyRole` modifier
/// @dev    Inheriting this automatically makes ROLES module a dependency
abstract contract RolesConsumer {
    ROLESv1 public ROLES;

    modifier onlyRole(bytes32 role_) {
        ROLES.requireRole(role_, msg.sender);
        _;
    }
}

/// @notice Module that holds multisig roles needed by various policies.
contract OlympusRoles is ROLESv1 {
    //============================================================================================//
    //                                        MODULE SETUP                                        //
    //============================================================================================//

    constructor(Kernel kernel_) Module(kernel_) {}

    /// @inheritdoc Module
    function KEYCODE() public pure override returns (Keycode) {
        return toKeycode("ROLES");
    }

    /// @inheritdoc Module
    function VERSION() external pure override returns (uint8 major, uint8 minor) {
        major = 1;
        minor = 0;
    }

    //============================================================================================//
    //                                       CORE FUNCTIONS                                       //
    //============================================================================================//

    /// @inheritdoc ROLESv1
    function saveRole(bytes32 role_, address addr_) external override permissioned {
        if (hasRole[addr_][role_]) revert ROLES_AddressAlreadyHasRole(addr_, role_);

        ensureValidRole(role_);

        // Grant role to the address
        hasRole[addr_][role_] = true;

        emit RoleGranted(role_, addr_);
    }

    /// @inheritdoc ROLESv1
    function removeRole(bytes32 role_, address addr_) external override permissioned {
        if (!hasRole[addr_][role_]) revert ROLES_AddressDoesNotHaveRole(addr_, role_);

        hasRole[addr_][role_] = false;

        emit RoleRevoked(role_, addr_);
    }

    //============================================================================================//
    //                                       VIEW FUNCTIONS                                       //
    //============================================================================================//

    /// @inheritdoc ROLESv1
    function requireRole(bytes32 role_, address caller_) external view override {
        if (!hasRole[caller_][role_]) revert ROLES_RequireRole(role_);
    }

    /// @inheritdoc ROLESv1
    function ensureValidRole(bytes32 role_) public pure override {
        for (uint256 i = 0; i < 32; ) {
            bytes1 char = role_[i];
            if ((char < 0x61 || char > 0x7A) && char != 0x5f && char != 0x00) {
                revert ROLES_InvalidRole(role_); // a-z only
            }
            unchecked {
                i++;
            }
        }
    }
}

File 8 of 17 : TRSRY.v1.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.15;

import {ERC20} from "solmate/tokens/ERC20.sol";
import "src/Kernel.sol";

/// @notice Treasury holds all other assets under the control of the protocol.
abstract contract TRSRYv1 is Module {
    // =========  EVENTS ========= //

    event IncreaseWithdrawApproval(
        address indexed withdrawer_,
        ERC20 indexed token_,
        uint256 newAmount_
    );
    event DecreaseWithdrawApproval(
        address indexed withdrawer_,
        ERC20 indexed token_,
        uint256 newAmount_
    );
    event Withdrawal(
        address indexed policy_,
        address indexed withdrawer_,
        ERC20 indexed token_,
        uint256 amount_
    );
    event IncreaseDebtorApproval(address indexed debtor_, ERC20 indexed token_, uint256 newAmount_);
    event DecreaseDebtorApproval(address indexed debtor_, ERC20 indexed token_, uint256 newAmount_);
    event DebtIncurred(ERC20 indexed token_, address indexed policy_, uint256 amount_);
    event DebtRepaid(ERC20 indexed token_, address indexed policy_, uint256 amount_);
    event DebtSet(ERC20 indexed token_, address indexed policy_, uint256 amount_);

    // =========  ERRORS ========= //

    error TRSRY_NoDebtOutstanding();
    error TRSRY_NotActive();

    // =========  STATE ========= //

    /// @notice Status of the treasury. If false, no withdrawals or debt can be incurred.
    bool public active;

    /// @notice Mapping of who is approved for withdrawal.
    /// @dev    withdrawer -> token -> amount. Infinite approval is max(uint256).
    mapping(address => mapping(ERC20 => uint256)) public withdrawApproval;

    /// @notice Mapping of who is approved to incur debt.
    /// @dev    debtor -> token -> amount. Infinite approval is max(uint256).
    mapping(address => mapping(ERC20 => uint256)) public debtApproval;

    /// @notice Total debt for token across all withdrawals.
    mapping(ERC20 => uint256) public totalDebt;

    /// @notice Debt for particular token and debtor address
    mapping(ERC20 => mapping(address => uint256)) public reserveDebt;

    // =========  FUNCTIONS ========= //

    modifier onlyWhileActive() {
        if (!active) revert TRSRY_NotActive();
        _;
    }

    /// @notice Increase approval for specific withdrawer addresses
    function increaseWithdrawApproval(
        address withdrawer_,
        ERC20 token_,
        uint256 amount_
    ) external virtual;

    /// @notice Decrease approval for specific withdrawer addresses
    function decreaseWithdrawApproval(
        address withdrawer_,
        ERC20 token_,
        uint256 amount_
    ) external virtual;

    /// @notice Allow withdrawal of reserve funds from pre-approved addresses.
    function withdrawReserves(address to_, ERC20 token_, uint256 amount_) external virtual;

    /// @notice Increase approval for someone to accrue debt in order to withdraw reserves.
    /// @dev    Debt will generally be taken by contracts to allocate treasury funds in yield sources.
    function increaseDebtorApproval(
        address debtor_,
        ERC20 token_,
        uint256 amount_
    ) external virtual;

    /// @notice Decrease approval for someone to withdraw reserves as debt.
    function decreaseDebtorApproval(
        address debtor_,
        ERC20 token_,
        uint256 amount_
    ) external virtual;

    /// @notice Pre-approved policies can get a loan to perform operations with treasury assets.
    function incurDebt(ERC20 token_, uint256 amount_) external virtual;

    /// @notice Repay a debtor debt.
    /// @dev    Only confirmed to safely handle standard and non-standard ERC20s.
    /// @dev    Can have unforeseen consequences with ERC777. Be careful with ERC777 as reserve.
    function repayDebt(address debtor_, ERC20 token_, uint256 amount_) external virtual;

    /// @notice An escape hatch for setting debt in special cases, like swapping reserves to another token.
    function setDebt(address debtor_, ERC20 token_, uint256 amount_) external virtual;

    /// @notice Get total balance of assets inside the treasury + any debt taken out against those assets.
    function getReserveBalance(ERC20 token_) external view virtual returns (uint256);

    /// @notice Emergency shutdown of withdrawals.
    function deactivate() external virtual;

    /// @notice Re-activate withdrawals after shutdown.
    function activate() external virtual;
}

File 9 of 17 : MINTR.v1.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.15;

import {OlympusERC20Token as OHM} from "src/external/OlympusERC20.sol";
import "src/Kernel.sol";

/// @notice Wrapper for minting and burning functions of OHM token.
abstract contract MINTRv1 is Module {
    // =========  EVENTS ========= //

    event IncreaseMintApproval(address indexed policy_, uint256 newAmount_);
    event DecreaseMintApproval(address indexed policy_, uint256 newAmount_);
    event Mint(address indexed policy_, address indexed to_, uint256 amount_);
    event Burn(address indexed policy_, address indexed from_, uint256 amount_);

    // ========= ERRORS ========= //

    error MINTR_NotApproved();
    error MINTR_ZeroAmount();
    error MINTR_NotActive();

    // =========  STATE ========= //

    OHM public ohm;

    /// @notice Status of the minter. If false, minting and burning OHM is disabled.
    bool public active;

    /// @notice Mapping of who is approved for minting.
    /// @dev    minter -> amount. Infinite approval is max(uint256).
    mapping(address => uint256) public mintApproval;

    // =========  FUNCTIONS ========= //

    modifier onlyWhileActive() {
        if (!active) revert MINTR_NotActive();
        _;
    }

    /// @notice Mint OHM to an address.
    function mintOhm(address to_, uint256 amount_) external virtual;

    /// @notice Burn OHM from an address. Must have approval.
    function burnOhm(address from_, uint256 amount_) external virtual;

    /// @notice Increase approval for specific withdrawer addresses
    /// @dev    Policies must explicity request how much they want approved before withdrawing.
    function increaseMintApproval(address policy_, uint256 amount_) external virtual;

    /// @notice Decrease approval for specific withdrawer addresses
    function decreaseMintApproval(address policy_, uint256 amount_) external virtual;

    /// @notice Emergency shutdown of minting and burning.
    function deactivate() external virtual;

    /// @notice Re-activate minting and burning after shutdown.
    function activate() external virtual;
}

File 10 of 17 : Kernel.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.15;

//     ███████    █████       █████ █████ ██████   ██████ ███████████  █████  █████  █████████
//   ███░░░░░███ ░░███       ░░███ ░░███ ░░██████ ██████ ░░███░░░░░███░░███  ░░███  ███░░░░░███
//  ███     ░░███ ░███        ░░███ ███   ░███░█████░███  ░███    ░███ ░███   ░███ ░███    ░░░
// ░███      ░███ ░███         ░░█████    ░███░░███ ░███  ░██████████  ░███   ░███ ░░█████████
// ░███      ░███ ░███          ░░███     ░███ ░░░  ░███  ░███░░░░░░   ░███   ░███  ░░░░░░░░███
// ░░███     ███  ░███      █    ░███     ░███      ░███  ░███         ░███   ░███  ███    ░███
//  ░░░███████░   ███████████    █████    █████     █████ █████        ░░████████  ░░█████████
//    ░░░░░░░    ░░░░░░░░░░░    ░░░░░    ░░░░░     ░░░░░ ░░░░░          ░░░░░░░░    ░░░░░░░░░

//============================================================================================//
//                                        GLOBAL TYPES                                        //
//============================================================================================//

/// @notice Actions to trigger state changes in the kernel. Passed by the executor
enum Actions {
    InstallModule,
    UpgradeModule,
    ActivatePolicy,
    DeactivatePolicy,
    ChangeExecutor,
    MigrateKernel
}

/// @notice Used by executor to select an action and a target contract for a kernel action
struct Instruction {
    Actions action;
    address target;
}

/// @notice Used to define which module functions a policy needs access to
struct Permissions {
    Keycode keycode;
    bytes4 funcSelector;
}

type Keycode is bytes5;

//============================================================================================//
//                                       UTIL FUNCTIONS                                       //
//============================================================================================//

error TargetNotAContract(address target_);
error InvalidKeycode(Keycode keycode_);

// solhint-disable-next-line func-visibility
function toKeycode(bytes5 keycode_) pure returns (Keycode) {
    return Keycode.wrap(keycode_);
}

// solhint-disable-next-line func-visibility
function fromKeycode(Keycode keycode_) pure returns (bytes5) {
    return Keycode.unwrap(keycode_);
}

// solhint-disable-next-line func-visibility
function ensureContract(address target_) view {
    if (target_.code.length == 0) revert TargetNotAContract(target_);
}

// solhint-disable-next-line func-visibility
function ensureValidKeycode(Keycode keycode_) pure {
    bytes5 unwrapped = Keycode.unwrap(keycode_);
    for (uint256 i = 0; i < 5; ) {
        bytes1 char = unwrapped[i];
        if (char < 0x41 || char > 0x5A) revert InvalidKeycode(keycode_); // A-Z only
        unchecked {
            i++;
        }
    }
}

//============================================================================================//
//                                        COMPONENTS                                          //
//============================================================================================//

/// @notice Generic adapter interface for kernel access in modules and policies.
abstract contract KernelAdapter {
    error KernelAdapter_OnlyKernel(address caller_);

    Kernel public kernel;

    constructor(Kernel kernel_) {
        kernel = kernel_;
    }

    /// @notice Modifier to restrict functions to be called only by kernel.
    modifier onlyKernel() {
        if (msg.sender != address(kernel)) revert KernelAdapter_OnlyKernel(msg.sender);
        _;
    }

    /// @notice Function used by kernel when migrating to a new kernel.
    function changeKernel(Kernel newKernel_) external onlyKernel {
        kernel = newKernel_;
    }
}

/// @notice Base level extension of the kernel. Modules act as independent state components to be
///         interacted with and mutated through policies.
/// @dev    Modules are installed and uninstalled via the executor.
abstract contract Module is KernelAdapter {
    error Module_PolicyNotPermitted(address policy_);

    constructor(Kernel kernel_) KernelAdapter(kernel_) {}

    /// @notice Modifier to restrict which policies have access to module functions.
    modifier permissioned() {
        if (
            msg.sender == address(kernel) ||
            !kernel.modulePermissions(KEYCODE(), Policy(msg.sender), msg.sig)
        ) revert Module_PolicyNotPermitted(msg.sender);
        _;
    }

    /// @notice 5 byte identifier for a module.
    function KEYCODE() public pure virtual returns (Keycode) {}

    /// @notice Returns which semantic version of a module is being implemented.
    /// @return major - Major version upgrade indicates breaking change to the interface.
    /// @return minor - Minor version change retains backward-compatible interface.
    function VERSION() external pure virtual returns (uint8 major, uint8 minor) {}

    /// @notice Initialization function for the module
    /// @dev    This function is called when the module is installed or upgraded by the kernel.
    /// @dev    MUST BE GATED BY onlyKernel. Used to encompass any initialization or upgrade logic.
    function INIT() external virtual onlyKernel {}
}

/// @notice Policies are application logic and external interface for the kernel and installed modules.
/// @dev    Policies are activated and deactivated in the kernel by the executor.
/// @dev    Module dependencies and function permissions must be defined in appropriate functions.
abstract contract Policy is KernelAdapter {
    error Policy_ModuleDoesNotExist(Keycode keycode_);

    constructor(Kernel kernel_) KernelAdapter(kernel_) {}

    /// @notice Easily accessible indicator for if a policy is activated or not.
    function isActive() external view returns (bool) {
        return kernel.isPolicyActive(this);
    }

    /// @notice Function to grab module address from a given keycode.
    function getModuleAddress(Keycode keycode_) internal view returns (address) {
        address moduleForKeycode = address(kernel.getModuleForKeycode(keycode_));
        if (moduleForKeycode == address(0)) revert Policy_ModuleDoesNotExist(keycode_);
        return moduleForKeycode;
    }

    /// @notice Define module dependencies for this policy.
    /// @return dependencies - Keycode array of module dependencies.
    function configureDependencies() external virtual returns (Keycode[] memory dependencies) {}

    /// @notice Function called by kernel to set module function permissions.
    /// @return requests - Array of keycodes and function selectors for requested permissions.
    function requestPermissions() external view virtual returns (Permissions[] memory requests) {}
}

/// @notice Main contract that acts as a central component registry for the protocol.
/// @dev    The kernel manages modules and policies. The kernel is mutated via predefined Actions,
/// @dev    which are input from any address assigned as the executor. The executor can be changed as needed.
contract Kernel {
    // =========  EVENTS ========= //

    event PermissionsUpdated(
        Keycode indexed keycode_,
        Policy indexed policy_,
        bytes4 funcSelector_,
        bool granted_
    );
    event ActionExecuted(Actions indexed action_, address indexed target_);

    // =========  ERRORS ========= //

    error Kernel_OnlyExecutor(address caller_);
    error Kernel_ModuleAlreadyInstalled(Keycode module_);
    error Kernel_InvalidModuleUpgrade(Keycode module_);
    error Kernel_PolicyAlreadyActivated(address policy_);
    error Kernel_PolicyNotActivated(address policy_);

    // =========  PRIVILEGED ADDRESSES ========= //

    /// @notice Address that is able to initiate Actions in the kernel. Can be assigned to a multisig or governance contract.
    address public executor;

    // =========  MODULE MANAGEMENT ========= //

    /// @notice Array of all modules currently installed.
    Keycode[] public allKeycodes;

    /// @notice Mapping of module address to keycode.
    mapping(Keycode => Module) public getModuleForKeycode;

    /// @notice Mapping of keycode to module address.
    mapping(Module => Keycode) public getKeycodeForModule;

    /// @notice Mapping of a keycode to all of its policy dependents. Used to efficiently reconfigure policy dependencies.
    mapping(Keycode => Policy[]) public moduleDependents;

    /// @notice Helper for module dependent arrays. Prevents the need to loop through array.
    mapping(Keycode => mapping(Policy => uint256)) public getDependentIndex;

    /// @notice Module <> Policy Permissions.
    /// @dev    Keycode -> Policy -> Function Selector -> bool for permission
    mapping(Keycode => mapping(Policy => mapping(bytes4 => bool))) public modulePermissions;

    // =========  POLICY MANAGEMENT ========= //

    /// @notice List of all active policies
    Policy[] public activePolicies;

    /// @notice Helper to get active policy quickly. Prevents need to loop through array.
    mapping(Policy => uint256) public getPolicyIndex;

    //============================================================================================//
    //                                       CORE FUNCTIONS                                       //
    //============================================================================================//

    constructor() {
        executor = msg.sender;
    }

    /// @notice Modifier to check if caller is the executor.
    modifier onlyExecutor() {
        if (msg.sender != executor) revert Kernel_OnlyExecutor(msg.sender);
        _;
    }

    function isPolicyActive(Policy policy_) public view returns (bool) {
        return activePolicies.length > 0 && activePolicies[getPolicyIndex[policy_]] == policy_;
    }

    /// @notice Main kernel function. Initiates state changes to kernel depending on Action passed in.
    function executeAction(Actions action_, address target_) external onlyExecutor {
        if (action_ == Actions.InstallModule) {
            ensureContract(target_);
            ensureValidKeycode(Module(target_).KEYCODE());
            _installModule(Module(target_));
        } else if (action_ == Actions.UpgradeModule) {
            ensureContract(target_);
            ensureValidKeycode(Module(target_).KEYCODE());
            _upgradeModule(Module(target_));
        } else if (action_ == Actions.ActivatePolicy) {
            ensureContract(target_);
            _activatePolicy(Policy(target_));
        } else if (action_ == Actions.DeactivatePolicy) {
            ensureContract(target_);
            _deactivatePolicy(Policy(target_));
        } else if (action_ == Actions.ChangeExecutor) {
            executor = target_;
        } else if (action_ == Actions.MigrateKernel) {
            ensureContract(target_);
            _migrateKernel(Kernel(target_));
        }

        emit ActionExecuted(action_, target_);
    }

    function _installModule(Module newModule_) internal {
        Keycode keycode = newModule_.KEYCODE();

        if (address(getModuleForKeycode[keycode]) != address(0))
            revert Kernel_ModuleAlreadyInstalled(keycode);

        getModuleForKeycode[keycode] = newModule_;
        getKeycodeForModule[newModule_] = keycode;
        allKeycodes.push(keycode);

        newModule_.INIT();
    }

    function _upgradeModule(Module newModule_) internal {
        Keycode keycode = newModule_.KEYCODE();
        Module oldModule = getModuleForKeycode[keycode];

        if (address(oldModule) == address(0) || oldModule == newModule_)
            revert Kernel_InvalidModuleUpgrade(keycode);

        getKeycodeForModule[oldModule] = Keycode.wrap(bytes5(0));
        getKeycodeForModule[newModule_] = keycode;
        getModuleForKeycode[keycode] = newModule_;

        newModule_.INIT();

        _reconfigurePolicies(keycode);
    }

    function _activatePolicy(Policy policy_) internal {
        if (isPolicyActive(policy_)) revert Kernel_PolicyAlreadyActivated(address(policy_));

        // Add policy to list of active policies
        activePolicies.push(policy_);
        getPolicyIndex[policy_] = activePolicies.length - 1;

        // Record module dependencies
        Keycode[] memory dependencies = policy_.configureDependencies();
        uint256 depLength = dependencies.length;

        for (uint256 i; i < depLength; ) {
            Keycode keycode = dependencies[i];

            moduleDependents[keycode].push(policy_);
            getDependentIndex[keycode][policy_] = moduleDependents[keycode].length - 1;

            unchecked {
                ++i;
            }
        }

        // Grant permissions for policy to access restricted module functions
        Permissions[] memory requests = policy_.requestPermissions();
        _setPolicyPermissions(policy_, requests, true);
    }

    function _deactivatePolicy(Policy policy_) internal {
        if (!isPolicyActive(policy_)) revert Kernel_PolicyNotActivated(address(policy_));

        // Revoke permissions
        Permissions[] memory requests = policy_.requestPermissions();
        _setPolicyPermissions(policy_, requests, false);

        // Remove policy from all policy data structures
        uint256 idx = getPolicyIndex[policy_];
        Policy lastPolicy = activePolicies[activePolicies.length - 1];

        activePolicies[idx] = lastPolicy;
        activePolicies.pop();
        getPolicyIndex[lastPolicy] = idx;
        delete getPolicyIndex[policy_];

        // Remove policy from module dependents
        _pruneFromDependents(policy_);
    }

    /// @notice All functionality will move to the new kernel. WARNING: ACTION WILL BRICK THIS KERNEL.
    /// @dev    New kernel must add in all of the modules and policies via executeAction.
    /// @dev    NOTE: Data does not get cleared from this kernel.
    function _migrateKernel(Kernel newKernel_) internal {
        uint256 keycodeLen = allKeycodes.length;
        for (uint256 i; i < keycodeLen; ) {
            Module module = Module(getModuleForKeycode[allKeycodes[i]]);
            module.changeKernel(newKernel_);
            unchecked {
                ++i;
            }
        }

        uint256 policiesLen = activePolicies.length;
        for (uint256 j; j < policiesLen; ) {
            Policy policy = activePolicies[j];

            // Deactivate before changing kernel
            policy.changeKernel(newKernel_);
            unchecked {
                ++j;
            }
        }
    }

    function _reconfigurePolicies(Keycode keycode_) internal {
        Policy[] memory dependents = moduleDependents[keycode_];
        uint256 depLength = dependents.length;

        for (uint256 i; i < depLength; ) {
            dependents[i].configureDependencies();

            unchecked {
                ++i;
            }
        }
    }

    function _setPolicyPermissions(
        Policy policy_,
        Permissions[] memory requests_,
        bool grant_
    ) internal {
        uint256 reqLength = requests_.length;
        for (uint256 i = 0; i < reqLength; ) {
            Permissions memory request = requests_[i];
            modulePermissions[request.keycode][policy_][request.funcSelector] = grant_;

            emit PermissionsUpdated(request.keycode, policy_, request.funcSelector, grant_);

            unchecked {
                ++i;
            }
        }
    }

    function _pruneFromDependents(Policy policy_) internal {
        Keycode[] memory dependencies = policy_.configureDependencies();
        uint256 depcLength = dependencies.length;

        for (uint256 i; i < depcLength; ) {
            Keycode keycode = dependencies[i];
            Policy[] storage dependents = moduleDependents[keycode];

            uint256 origIndex = getDependentIndex[keycode][policy_];
            Policy lastPolicy = dependents[dependents.length - 1];

            // Swap with last and pop
            dependents[origIndex] = lastPolicy;
            dependents.pop();

            // Record new index and delete deactivated policy index
            getDependentIndex[keycode][lastPolicy] = origIndex;
            delete getDependentIndex[keycode][policy_];

            unchecked {
                ++i;
            }
        }
    }
}

File 11 of 17 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 12 of 17 : FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

File 13 of 17 : ClonesWithImmutableArgs.sol
// SPDX-License-Identifier: BSD

pragma solidity ^0.8.4;

/// @title ClonesWithImmutableArgs
/// @author wighawag, zefram.eth
/// @notice Enables creating clone contracts with immutable args
library ClonesWithImmutableArgs {
    error CreateFail();

    /// @notice Creates a clone proxy of the implementation contract, with immutable args
    /// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length
    /// @param implementation The implementation contract to clone
    /// @param data Encoded immutable args
    /// @return instance The address of the created clone
    function clone(address implementation, bytes memory data)
        internal
        returns (address instance)
    {
        // unrealistic for memory ptr or data length to exceed 256 bits
        unchecked {
            uint256 extraLength = data.length + 2; // +2 bytes for telling how much data there is appended to the call
            uint256 creationSize = 0x43 + extraLength;
            uint256 runSize = creationSize - 11;
            uint256 dataPtr;
            uint256 ptr;
            // solhint-disable-next-line no-inline-assembly
            assembly {
                ptr := mload(0x40)

                // -------------------------------------------------------------------------------------------------------------
                // CREATION (11 bytes)
                // -------------------------------------------------------------------------------------------------------------

                // 3d          | RETURNDATASIZE        | 0                       | –
                // 61 runtime  | PUSH2 runtime (r)     | r 0                     | –
                mstore(
                    ptr,
                    0x3d61000000000000000000000000000000000000000000000000000000000000
                )
                mstore(add(ptr, 0x02), shl(240, runSize)) // size of the contract running bytecode (16 bits)

                // creation size = 0b
                // 80          | DUP1                  | r r 0                   | –
                // 60 creation | PUSH1 creation (c)    | c r r 0                 | –
                // 3d          | RETURNDATASIZE        | 0 c r r 0               | –
                // 39          | CODECOPY              | r 0                     | [0-2d]: runtime code
                // 81          | DUP2                  | 0 c  0                  | [0-2d]: runtime code
                // f3          | RETURN                | 0                       | [0-2d]: runtime code
                mstore(
                    add(ptr, 0x04),
                    0x80600b3d3981f300000000000000000000000000000000000000000000000000
                )

                // -------------------------------------------------------------------------------------------------------------
                // RUNTIME
                // -------------------------------------------------------------------------------------------------------------

                // 36          | CALLDATASIZE          | cds                     | –
                // 3d          | RETURNDATASIZE        | 0 cds                   | –
                // 3d          | RETURNDATASIZE        | 0 0 cds                 | –
                // 37          | CALLDATACOPY          | –                       | [0, cds] = calldata
                // 61          | PUSH2 extra           | extra                   | [0, cds] = calldata
                mstore(
                    add(ptr, 0x0b),
                    0x363d3d3761000000000000000000000000000000000000000000000000000000
                )
                mstore(add(ptr, 0x10), shl(240, extraLength))

                // 60 0x38     | PUSH1 0x38            | 0x38 extra              | [0, cds] = calldata // 0x38 (56) is runtime size - data
                // 36          | CALLDATASIZE          | cds 0x38 extra          | [0, cds] = calldata
                // 39          | CODECOPY              | _                       | [0, cds] = calldata
                // 3d          | RETURNDATASIZE        | 0                       | [0, cds] = calldata
                // 3d          | RETURNDATASIZE        | 0 0                     | [0, cds] = calldata
                // 3d          | RETURNDATASIZE        | 0 0 0                   | [0, cds] = calldata
                // 36          | CALLDATASIZE          | cds 0 0 0               | [0, cds] = calldata
                // 61 extra    | PUSH2 extra           | extra cds 0 0 0         | [0, cds] = calldata
                mstore(
                    add(ptr, 0x12),
                    0x603836393d3d3d36610000000000000000000000000000000000000000000000
                )
                mstore(add(ptr, 0x1b), shl(240, extraLength))

                // 01          | ADD                   | cds+extra 0 0 0         | [0, cds] = calldata
                // 3d          | RETURNDATASIZE        | 0 cds 0 0 0             | [0, cds] = calldata
                // 73 addr     | PUSH20 0x123…         | addr 0 cds 0 0 0        | [0, cds] = calldata
                mstore(
                    add(ptr, 0x1d),
                    0x013d730000000000000000000000000000000000000000000000000000000000
                )
                mstore(add(ptr, 0x20), shl(0x60, implementation))

                // 5a          | GAS                   | gas addr 0 cds 0 0 0    | [0, cds] = calldata
                // f4          | DELEGATECALL          | success 0               | [0, cds] = calldata
                // 3d          | RETURNDATASIZE        | rds success 0           | [0, cds] = calldata
                // 82          | DUP3                  | 0 rds success 0         | [0, cds] = calldata
                // 80          | DUP1                  | 0 0 rds success 0       | [0, cds] = calldata
                // 3e          | RETURNDATACOPY        | success 0               | [0, rds] = return data (there might be some irrelevant leftovers in memory [rds, cds] when rds < cds)
                // 90          | SWAP1                 | 0 success               | [0, rds] = return data
                // 3d          | RETURNDATASIZE        | rds 0 success           | [0, rds] = return data
                // 91          | SWAP2                 | success 0 rds           | [0, rds] = return data
                // 60 0x36     | PUSH1 0x36            | 0x36 sucess 0 rds       | [0, rds] = return data
                // 57          | JUMPI                 | 0 rds                   | [0, rds] = return data
                // fd          | REVERT                | –                       | [0, rds] = return data
                // 5b          | JUMPDEST              | 0 rds                   | [0, rds] = return data
                // f3          | RETURN                | –                       | [0, rds] = return data

                mstore(
                    add(ptr, 0x34),
                    0x5af43d82803e903d91603657fd5bf30000000000000000000000000000000000
                )
            }

            // -------------------------------------------------------------------------------------------------------------
            // APPENDED DATA (Accessible from extcodecopy)
            // (but also send as appended data to the delegatecall)
            // -------------------------------------------------------------------------------------------------------------

            extraLength -= 2;
            uint256 counter = extraLength;
            uint256 copyPtr = ptr + 0x43;
            // solhint-disable-next-line no-inline-assembly
            assembly {
                dataPtr := add(data, 32)
            }
            for (; counter >= 32; counter -= 32) {
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    mstore(copyPtr, mload(dataPtr))
                }

                copyPtr += 32;
                dataPtr += 32;
            }
            uint256 mask = ~(256**(32 - counter) - 1);
            // solhint-disable-next-line no-inline-assembly
            assembly {
                mstore(copyPtr, and(mload(dataPtr), mask))
            }
            copyPtr += counter;
            // solhint-disable-next-line no-inline-assembly
            assembly {
                mstore(copyPtr, shl(240, extraLength))
            }
            // solhint-disable-next-line no-inline-assembly
            assembly {
                instance := create(0, ptr, creationSize)
            }
            if (instance == address(0)) {
                revert CreateFail();
            }
        }
    }
}

File 14 of 17 : Cooler.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {Clone} from "clones/Clone.sol";

import {CoolerFactory} from "./CoolerFactory.sol";
import {CoolerCallback} from "./CoolerCallback.sol";

// Function sig taken from gOHM contract
interface IDelegate { function delegate(address to_) external; }


/// @title  Cooler Loans.
/// @notice A Cooler is a smart contract escrow that facilitates fixed-duration, peer-to-peer
///         loans for a user-defined debt-collateral pair.
/// @dev    This contract uses Clones (https://github.com/wighawag/clones-with-immutable-args)
///         to save gas on deployment.
contract Cooler is Clone {
    using SafeTransferLib for ERC20;

    // --- ERRORS ----------------------------------------------------

    error OnlyApproved();
    error Deactivated();
    error Default();
    error NotExpired();
    error NotCoolerCallback();

    // --- DATA STRUCTURES -------------------------------------------

    /// @notice A loan begins with a borrow request.
    struct Request {
        uint256 amount;             // Amount to be borrowed.
        uint256 interest;           // Annualized percentage to be paid as interest.
        uint256 loanToCollateral;   // Requested loan-to-collateral ratio.
        uint256 duration;           // Time to repay the loan before it defaults.
        bool active;                // Any lender can clear an active loan request.
        address requester;          // The address that created the request.
    }

    /// @notice A request is converted to a loan when a lender clears it.
    struct Loan {
        Request request;        // Loan terms specified in the request.
        uint256 principal;      // Amount of principal debt owed to the lender.
        uint256 interestDue;    // Interest owed to the lender.
        uint256 collateral;     // Amount of collateral pledged.
        uint256 expiry;         // Time when the loan defaults.
        address lender;         // Lender's address.
        address recipient;      // Recipient of repayments.
        bool callback;          // If this is true, the lender must inherit CoolerCallback.
    }

    // --- IMMUTABLES ------------------------------------------------

    // This makes the code look prettier.
    uint256 private constant DECIMALS_INTEREST = 1e18;

    /// @notice This address owns the collateral in escrow.
    function owner() public pure returns (address _owner) {
        return _getArgAddress(0x0);
    }

    /// @notice This token is borrowed against.
    function collateral() public pure returns (ERC20 _collateral) {
        return ERC20(_getArgAddress(0x14));
    }

    /// @notice This token is lent.
    function debt() public pure returns (ERC20 _debt) {
        return ERC20(_getArgAddress(0x28));
    }
    
    /// @notice This contract created the Cooler
    function factory() public pure returns (CoolerFactory _factory) {
        return CoolerFactory(_getArgAddress(0x3c));
    }

    // --- STATE VARIABLES -------------------------------------------

    /// @notice Arrays stores all the loan requests.
    Request[] public requests;

    /// @notice Arrays stores all the granted loans.
    Loan[] public loans;

    /// @notice Facilitates transfer of lender ownership to new addresses
    mapping(uint256 => address) public approvals;

    // --- BORROWER --------------------------------------------------

    /// @notice Request a loan with given parameters.
    ///         Collateral is taken at time of request.
    /// @param  amount_ of debt tokens to borrow.
    /// @param  interest_ to pay (annualized % of 'amount_'). Expressed in DECIMALS_INTEREST.
    /// @param  loanToCollateral_ debt tokens per collateral token pledged. Expressed in 10**collateral().decimals().
    /// @param  duration_ of loan tenure in seconds.
    /// @return reqID of the created request. Equivalent to the index of request in requests[].
    function requestLoan(
        uint256 amount_,
        uint256 interest_,
        uint256 loanToCollateral_,
        uint256 duration_
    ) external returns (uint256 reqID) {
        reqID = requests.length;
        requests.push(
            Request({
                amount: amount_,
                interest: interest_,
                loanToCollateral: loanToCollateral_,
                duration: duration_,
                active: true,
                requester: msg.sender
            })
        );

        // The collateral is taken upfront. Will be escrowed
        // until the loan is repaid or defaulted.
        collateral().safeTransferFrom(
            msg.sender,
            address(this),
            collateralFor(amount_, loanToCollateral_)
        );

        // Log the event.
        factory().logRequestLoan(reqID);
    }

    /// @notice Cancel a loan request and get the collateral back.
    /// @param  reqID_ index of request in requests[].
    function rescindRequest(uint256 reqID_) external {
        if (msg.sender != owner()) revert OnlyApproved();

        Request storage req = requests[reqID_];

        if (!req.active) revert Deactivated();

        // Update storage and send collateral back to the owner.
        req.active = false;
        collateral().safeTransfer(owner(), collateralFor(req.amount, req.loanToCollateral));

        // Log the event.
        factory().logRescindRequest(reqID_);
    }

    /// @notice Repay a loan to get the collateral back.
    /// @dev    Despite a malicious lender could reenter with the callback, the
    ///         usage of `msg.sender` prevents any economical benefit to the
    ///         attacker, since they would be repaying the loan themselves.
    /// @param  loanID_ index of loan in loans[].
    /// @param  repayment_ debt tokens to be repaid.
    /// @return collateral given back to the borrower.
    function repayLoan(uint256 loanID_, uint256 repayment_) external returns (uint256) {
        Loan memory loan = loans[loanID_];

        if (block.timestamp > loan.expiry) revert Default();

        // Cap the repayment to the total debt of the loan
        uint256 totalDebt = loan.principal + loan.interestDue;
        if (repayment_ > totalDebt) repayment_ = totalDebt;

        // Need to repay interest first, then any extra goes to paying down principal.
        uint256 interestPaid;
        uint256 remainder;
        if (repayment_ >= loan.interestDue) {
            remainder = repayment_ - loan.interestDue;
            interestPaid = loan.interestDue;
            loan.interestDue = 0;
        } else {
            loan.interestDue -= repayment_;
            interestPaid = repayment_;
        }

        // We pay back only if user has paid back principal. This can be 0.
        uint256 decollateralized;
        if (remainder > 0) {
            decollateralized = (loan.collateral * remainder) / loan.principal;
            
            loan.principal -= remainder;
            loan.collateral -= decollateralized;
        }

        // Save updated loan info in storage.
        loans[loanID_] = loan;

        // Transfer repaid debt back to the lender and collateral back to the owner if applicable
        debt().safeTransferFrom(msg.sender, loan.recipient, repayment_);
        if (decollateralized > 0) collateral().safeTransfer(owner(), decollateralized);

        // Log the event.
        factory().logRepayLoan(loanID_, repayment_);

        // If necessary, trigger lender callback.
        if (loan.callback) {
            CoolerCallback(loan.lender).onRepay(loanID_, remainder, interestPaid);
        }

        return decollateralized;
    }

    /// @notice Delegate voting power on collateral.
    /// @param  to_ address to delegate.
    function delegateVoting(address to_) external {
        if (msg.sender != owner()) revert OnlyApproved();
        IDelegate(address(collateral())).delegate(to_);
    }

    // --- LENDER ----------------------------------------------------

    /// @notice Fill a requested loan as a lender.
    /// @param  reqID_ index of request in requests[].
    /// @param  recipient_ address to repay the loan to.
    /// @param  isCallback_ true if the lender implements the CoolerCallback abstract. False otherwise.
    /// @return loanID of the granted loan. Equivalent to the index of loan in loans[].
    function clearRequest(
        uint256 reqID_,
        address recipient_,
        bool isCallback_
    ) external returns (uint256 loanID) {
        Request memory req = requests[reqID_];

        // Loan callbacks are only allowed if:
        //  1. The loan request has been created via a trusted lender.
        //  2. The lender signals that it implements the CoolerCallback Abstract.
        bool callback = (isCallback_ && msg.sender == req.requester);

        // If necessary, ensure lender implements the CoolerCallback abstract.
        if (callback && !CoolerCallback(msg.sender).isCoolerCallback()) revert NotCoolerCallback();
        // Ensure loan request is active. 
        if (!req.active) revert Deactivated();

        // Clear the loan request in memory.
        req.active = false;

        // Calculate and store loan terms.
        uint256 interest = interestFor(req.amount, req.interest, req.duration);
        uint256 collat = collateralFor(req.amount, req.loanToCollateral);
        loanID = loans.length;

        loans.push(
            Loan({
                request: req,
                principal: req.amount,
                interestDue: interest,
                collateral: collat,
                expiry: block.timestamp + req.duration,
                lender: msg.sender,
                recipient: recipient_,
                callback: callback
            })
        );

        // Clear the loan request storage.
        requests[reqID_].active = false;

        // Transfer debt tokens to the owner of the request.
        debt().safeTransferFrom(msg.sender, owner(), req.amount);

        // Log the event.
        factory().logClearRequest(reqID_, loanID);
    }

    /// @notice Allow lender to extend a loan for the borrower. Doesn't require
    ///         borrower permission because it doesn't have a negative impact for them.
    /// @dev    Since this function solely impacts the expiration day, the lender
    ///         should ensure that extension interest payments are done beforehand.
    /// @param  loanID_ index of loan in loans[].
    /// @param  times_ that the fixed-term loan duration is extended.
    function extendLoanTerms(uint256 loanID_, uint8 times_) external {
        Loan memory loan = loans[loanID_];

        if (msg.sender != loan.lender) revert OnlyApproved();
        if (block.timestamp > loan.expiry) revert Default();

        // Update loan terms to reflect the extension.
        loan.expiry += loan.request.duration * times_;

        // Save updated loan info in storage.
        loans[loanID_] = loan;

        // Log the event.
        factory().logExtendLoan(loanID_, times_);
    }

    /// @notice Claim collateral upon loan default.
    /// @param  loanID_ index of loan in loans[].
    /// @return defaulted debt by the borrower, collateral kept by the lender, elapsed time since expiry.
    function claimDefaulted(uint256 loanID_) external returns (uint256, uint256, uint256, uint256) {
        Loan memory loan = loans[loanID_];

        if (block.timestamp <= loan.expiry) revert NotExpired();

        loans[loanID_].principal = 0;
        loans[loanID_].interestDue = 0;
        loans[loanID_].collateral = 0;

        // Transfer defaulted collateral to the lender.
        collateral().safeTransfer(loan.lender, loan.collateral);

        // Log the event.
        factory().logDefaultLoan(loanID_, loan.collateral);

        // If necessary, trigger lender callback.
        if (loan.callback) {
            CoolerCallback(loan.lender).onDefault(loanID_, loan.principal, loan.interestDue, loan.collateral);
        }

        return (loan.principal, loan.interestDue, loan.collateral, block.timestamp - loan.expiry);
    }

    /// @notice Approve transfer of loan ownership rights to a new address.
    /// @param  to_ address to be approved.
    /// @param  loanID_ index of loan in loans[].
    function approveTransfer(address to_, uint256 loanID_) external {
        if (msg.sender != loans[loanID_].lender) revert OnlyApproved();

        // Update transfer approvals.
        approvals[loanID_] = to_;
    }

    /// @notice Execute loan ownership transfer. Must be previously approved by the lender.
    /// @param  loanID_ index of loan in loans[].
    function transferOwnership(uint256 loanID_) external {
        if (msg.sender != approvals[loanID_]) revert OnlyApproved();

        // Update the load lender and the recipient.
        loans[loanID_].lender = msg.sender;
        loans[loanID_].recipient = msg.sender;
        // Callbacks are disabled when transferring ownership.
        loans[loanID_].callback = false;
        // Clear transfer approvals.
        approvals[loanID_] = address(0);
    }

    /// @notice Allow lender to set repayment recipient of a given loan.
    /// @param  loanID_ of lender's loan.
    /// @param  recipient_ reciever of repayments
    function setRepaymentAddress(uint256 loanID_, address recipient_) external {
        if (msg.sender != loans[loanID_].lender) revert OnlyApproved();

        // Update the repayment method.
        loans[loanID_].recipient = recipient_;
    }

    // --- AUX FUNCTIONS ---------------------------------------------

    /// @notice Compute collateral needed for a desired loan amount at given loan to collateral ratio.
    /// @param  principal_ amount of debt tokens.
    /// @param  loanToCollateral_ ratio for loan. Expressed in 10**collateral().decimals().
    function collateralFor(uint256 principal_, uint256 loanToCollateral_) public view returns (uint256) {
        return (principal_ * (10 ** collateral().decimals())) / loanToCollateral_;
    }

    /// @notice Compute interest cost on amount for duration at given annualized rate.
    /// @param  principal_ amount of debt tokens.
    /// @param  rate_ of interest (annualized).
    /// @param  duration_ of the loan in seconds.
    /// @return Interest in debt token terms.
    function interestFor(uint256 principal_, uint256 rate_, uint256 duration_) public pure returns (uint256) {
        uint256 interest = (rate_ * duration_) / 365 days;
        return (principal_ * interest) / DECIMALS_INTEREST;
    }

    /// @notice Check if given loan has expired.
    /// @param  loanID_ index of loan in loans[].
    /// @return Expiration status.
    function hasExpired(uint256 loanID_) external view returns (bool) {
        return block.timestamp > loans[loanID_].expiry;
    }

    /// @notice Check if a given request is active.
    /// @param  reqID_ index of request in requests[].
    /// @return Active status.
    function isActive(uint256 reqID_) external view returns (bool) {
        return requests[reqID_].active;
    }

    /// @notice Getter for Request data as a struct.
    /// @param  reqID_ index of request in requests[].
    /// @return Request struct.
    function getRequest(uint256 reqID_) external view returns (Request memory) {
        return requests[reqID_];
    }

    /// @notice Getter for Loan data as a struct.
    /// @param loanID_ index of loan in loans[].
    /// @return Loan struct.
    function getLoan(uint256 loanID_) external view returns (Loan memory) {
        return loans[loanID_];
    }
}

File 15 of 17 : ROLES.v1.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.15;

import "src/Kernel.sol";

abstract contract ROLESv1 is Module {
    // =========  EVENTS ========= //

    event RoleGranted(bytes32 indexed role_, address indexed addr_);
    event RoleRevoked(bytes32 indexed role_, address indexed addr_);

    // =========  ERRORS ========= //

    error ROLES_InvalidRole(bytes32 role_);
    error ROLES_RequireRole(bytes32 role_);
    error ROLES_AddressAlreadyHasRole(address addr_, bytes32 role_);
    error ROLES_AddressDoesNotHaveRole(address addr_, bytes32 role_);
    error ROLES_RoleDoesNotExist(bytes32 role_);

    // =========  STATE ========= //

    /// @notice Mapping for if an address has a policy-defined role.
    mapping(address => mapping(bytes32 => bool)) public hasRole;

    // =========  FUNCTIONS ========= //

    /// @notice Function to grant policy-defined roles to some address. Can only be called by admin.
    function saveRole(bytes32 role_, address addr_) external virtual;

    /// @notice Function to revoke policy-defined roles from some address. Can only be called by admin.
    function removeRole(bytes32 role_, address addr_) external virtual;

    /// @notice "Modifier" to restrict policy function access to certain addresses with a role.
    /// @dev    Roles are defined in the policy and granted by the ROLES admin.
    function requireRole(bytes32 role_, address caller_) external virtual;

    /// @notice Function that checks if role is valid (all lower case)
    function ensureValidRole(bytes32 role_) external pure virtual;
}

File 16 of 17 : OlympusERC20.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.7.5;

/// @notice Olympus OHM token
/// @dev This contract is the legacy v2 OHM token. Included in the repo for completeness,
///      since it is not being changed and is imported in some contracts.

interface IOlympusAuthority {
    // =========  EVENTS ========= //

    event GovernorPushed(address indexed from, address indexed to, bool _effectiveImmediately);
    event GuardianPushed(address indexed from, address indexed to, bool _effectiveImmediately);
    event PolicyPushed(address indexed from, address indexed to, bool _effectiveImmediately);
    event VaultPushed(address indexed from, address indexed to, bool _effectiveImmediately);

    event GovernorPulled(address indexed from, address indexed to);
    event GuardianPulled(address indexed from, address indexed to);
    event PolicyPulled(address indexed from, address indexed to);
    event VaultPulled(address indexed from, address indexed to);

    // =========  VIEW ========= //

    function governor() external view returns (address);

    function guardian() external view returns (address);

    function policy() external view returns (address);

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

// File: types/OlympusAccessControlled.sol

abstract contract OlympusAccessControlled {
    // =========  EVENTS ========= //

    event AuthorityUpdated(IOlympusAuthority indexed authority);

    string internal UNAUTHORIZED = "UNAUTHORIZED"; // save gas

    // =========  STATE VARIABLES ========= //

    IOlympusAuthority public authority;

    // =========  Constructor ========= //

    constructor(IOlympusAuthority _authority) {
        authority = _authority;
        emit AuthorityUpdated(_authority);
    }

    // =========  MODIFIERS ========= //

    modifier onlyGovernor() {
        require(msg.sender == authority.governor(), UNAUTHORIZED);
        _;
    }

    modifier onlyGuardian() {
        require(msg.sender == authority.guardian(), UNAUTHORIZED);
        _;
    }

    modifier onlyPermitted() {
        require(msg.sender == authority.policy(), UNAUTHORIZED);
        _;
    }

    modifier onlyVault() {
        require(msg.sender == authority.vault(), UNAUTHORIZED);
        _;
    }

    // =========  GOV ONLY ========= //

    function setAuthority(IOlympusAuthority _newAuthority) external onlyGovernor {
        authority = _newAuthority;
        emit AuthorityUpdated(_newAuthority);
    }
}

// File: cryptography/ECDSA.sol

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes memory signature
    ) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s;
        uint8 v;
        assembly {
            s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
            v := add(shr(255, vs), 27)
        }
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(
        bytes32 domainSeparator,
        bytes32 structHash
    ) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

// File: cryptography/EIP712.sol

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 */
abstract contract EIP712 {
    /* solhint-disable var-name-mixedcase */
    // 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;
    bytes32 private immutable _TYPE_HASH;

    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        uint256 chainID;
        assembly {
            chainID := chainid()
        }

        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        bytes32 typeHash = keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
        _CACHED_CHAIN_ID = chainID;
        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
        _TYPE_HASH = typeHash;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        uint256 chainID;
        assembly {
            chainID := chainid()
        }

        if (chainID == _CACHED_CHAIN_ID) {
            return _CACHED_DOMAIN_SEPARATOR;
        } else {
            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
        }
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        uint256 chainID;
        assembly {
            chainID := chainid()
        }

        return keccak256(abi.encode(typeHash, nameHash, versionHash, chainID, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }
}

// File: interfaces/IERC20Permit.sol

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as th xe allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

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

// File: interfaces/IERC20.sol

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

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

    /**
     * @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: interfaces/IOHM.sol

interface IOHM is IERC20 {
    function mint(address account_, uint256 amount_) external;

    function burn(uint256 amount) external;

    function burnFrom(address account_, uint256 amount_) external;
}

// File: libraries/SafeMath.sol

library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        assert(a == b * c + (a % b)); // There is no case in which this doesn't hold

        return c;
    }

    // Only used in the  BondingCalculator.sol
    function sqrrt(uint256 a) internal pure returns (uint256 c) {
        if (a > 3) {
            c = a;
            uint256 b = add(div(a, 2), 1);
            while (b < c) {
                c = b;
                b = div(add(div(a, b), b), 2);
            }
        } else if (a != 0) {
            c = 1;
        }
    }
}

// File: libraries/Counters.sol

library Counters {
    using SafeMath for uint256;

    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        // The {SafeMath} overflow check can be skipped here, see the comment at the top
        counter._value += 1;
    }

    function decrement(Counter storage counter) internal {
        counter._value = counter._value.sub(1);
    }
}

// File: types/ERC20.sol

abstract contract ERC20 is IERC20 {
    using SafeMath for uint256;

    // TODO comment actual hash value.
    bytes32 private constant ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256("ERC20Token");

    mapping(address => uint256) internal _balances;

    mapping(address => mapping(address => uint256)) internal _allowances;

    uint256 internal _totalSupply;

    string internal _name;

    string internal _symbol;

    uint8 internal immutable _decimals;

    constructor(string memory name_, string memory symbol_, uint8 decimals_) {
        _name = name_;
        _symbol = symbol_;
        _decimals = decimals_;
    }

    function name() public view returns (string memory) {
        return _name;
    }

    function symbol() public view returns (string memory) {
        return _symbol;
    }

    function decimals() public view virtual returns (uint8) {
        return _decimals;
    }

    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    function allowance(
        address owner,
        address spender
    ) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(
            sender,
            msg.sender,
            _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance")
        );
        return true;
    }

    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
        return true;
    }

    function decreaseAllowance(
        address spender,
        uint256 subtractedValue
    ) public virtual returns (bool) {
        _approve(
            msg.sender,
            spender,
            _allowances[msg.sender][spender].sub(
                subtractedValue,
                "ERC20: decreased allowance below zero"
            )
        );
        return true;
    }

    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");
        _beforeTokenTransfer(address(0), account, amount);
        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function _beforeTokenTransfer(address from_, address to_, uint256 amount_) internal virtual {}
}

// File: types/ERC20Permit.sol

/**
 * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * _Available since v3.4._
 */
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
    using Counters for Counters.Counter;

    mapping(address => Counters.Counter) private _nonces;

    // solhint-disable-next-line var-name-mixedcase
    bytes32 private immutable _PERMIT_TYPEHASH =
        keccak256(
            "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
        );

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
     */
    constructor(string memory name) EIP712(name, "1") {}

    /**
     * @dev See {IERC20Permit-permit}.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");

        bytes32 structHash = keccak256(
            abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)
        );

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        require(signer == owner, "ERC20Permit: invalid signature");

        _approve(owner, spender, value);
    }

    /**
     * @dev See {IERC20Permit-nonces}.
     */
    function nonces(address owner) public view virtual override returns (uint256) {
        return _nonces[owner].current();
    }

    /**
     * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
        return _domainSeparatorV4();
    }

    /**
     * @dev "Consume a nonce": return the current value and increment.
     *
     * _Available since v4.1._
     */
    function _useNonce(address owner) internal virtual returns (uint256 current) {
        Counters.Counter storage nonce = _nonces[owner];
        current = nonce.current();
        nonce.increment();
    }
}

// File: OlympusERC20.sol

contract OlympusERC20Token is ERC20Permit, IOHM, OlympusAccessControlled {
    using SafeMath for uint256;

    constructor(
        address _authority
    )
        ERC20("Olympus", "OHM", 9)
        ERC20Permit("Olympus")
        OlympusAccessControlled(IOlympusAuthority(_authority))
    {}

    function mint(address account_, uint256 amount_) external override onlyVault {
        _mint(account_, amount_);
    }

    function burn(uint256 amount) external override {
        _burn(msg.sender, amount);
    }

    function burnFrom(address account_, uint256 amount_) external override {
        _burnFrom(account_, amount_);
    }

    function _burnFrom(address account_, uint256 amount_) internal {
        uint256 decreasedAllowance_ = allowance(account_, msg.sender).sub(
            amount_,
            "ERC20: burn amount exceeds allowance"
        );

        _approve(account_, msg.sender, decreasedAllowance_);
        _burn(account_, amount_);
    }
}

File 17 of 17 : Clone.sol
// SPDX-License-Identifier: BSD
pragma solidity ^0.8.4;

/// @title Clone
/// @author zefram.eth
/// @notice Provides helper functions for reading immutable args from calldata
contract Clone {
    /// @notice Reads an immutable arg with type address
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgAddress(uint256 argOffset)
        internal
        pure
        returns (address arg)
    {
        uint256 offset = _getImmutableArgsOffset();
        assembly {
            arg := shr(0x60, calldataload(add(offset, argOffset)))
        }
    }

    /// @notice Reads an immutable arg with type uint256
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint256(uint256 argOffset)
        internal
        pure
        returns (uint256 arg)
    {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := calldataload(add(offset, argOffset))
        }
    }

    /// @notice Reads an immutable arg with type uint64
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint64(uint256 argOffset)
        internal
        pure
        returns (uint64 arg)
    {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := shr(0xc0, calldataload(add(offset, argOffset)))
        }
    }

    /// @notice Reads an immutable arg with type uint8
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := shr(0xf8, calldataload(add(offset, argOffset)))
        }
    }

    /// @return offset The offset of the packed immutable args in calldata
    function _getImmutableArgsOffset() internal pure returns (uint256 offset) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            offset := sub(
                calldatasize(),
                add(shr(240, calldataload(sub(calldatasize(), 2))), 2)
            )
        }
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "interfaces/=src/interfaces/",
    "modules/=src/modules/",
    "policies/=src/policies/",
    "libraries/=src/libraries/",
    "solmate/=lib/solmate/src/",
    "layer-zero/=lib/solidity-examples/contracts/",
    "bonds/=lib/bonds/src/",
    "test/=src/test/",
    "clones/=lib/clones-with-immutable-args/src/",
    "cooler/=lib/Cooler/src/",
    "balancer-v2/=lib/balancer-v2/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "Cooler/=lib/Cooler/src/",
    "clones-with-immutable-args/=lib/clones-with-immutable-args/src/",
    "olympus-v3/=lib/Cooler/lib/olympus-v3/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solidity-examples/=lib/solidity-examples/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"ohm_","type":"address"},{"internalType":"address","name":"gohm_","type":"address"},{"internalType":"address","name":"staking_","type":"address"},{"internalType":"address","name":"sdai_","type":"address"},{"internalType":"address","name":"coolerFactory_","type":"address"},{"internalType":"address","name":"kernel_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BadEscrow","type":"error"},{"inputs":[],"name":"DurationMaximum","type":"error"},{"inputs":[{"internalType":"address","name":"caller_","type":"address"}],"name":"KernelAdapter_OnlyKernel","type":"error"},{"inputs":[],"name":"LengthDiscrepancy","type":"error"},{"inputs":[],"name":"NotLender","type":"error"},{"inputs":[],"name":"OnlyBorrower","type":"error"},{"inputs":[],"name":"OnlyBurnable","type":"error"},{"inputs":[],"name":"OnlyFromFactory","type":"error"},{"inputs":[{"internalType":"Keycode","name":"keycode_","type":"bytes5"}],"name":"Policy_ModuleDoesNotExist","type":"error"},{"inputs":[],"name":"TooEarlyToFund","type":"error"},{"anonymous":false,"inputs":[],"name":"Deactivate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Defund","type":"event"},{"anonymous":false,"inputs":[],"name":"Reactivate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"defund","type":"bool"},{"indexed":false,"internalType":"uint256","name":"daiAmount","type":"uint256"}],"name":"Rebalance","type":"event"},{"inputs":[],"name":"DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FUND_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FUND_CADENCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INTEREST_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LOAN_TO_COLLATERAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTR","outputs":[{"internalType":"contract MINTRv1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLES","outputs":[{"internalType":"contract ROLESv1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRSRY","outputs":[{"internalType":"contract TRSRYv1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"active","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Kernel","name":"newKernel_","type":"address"}],"name":"changeKernel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"coolers_","type":"address[]"},{"internalType":"uint256[]","name":"loans_","type":"uint256[]"}],"name":"claimDefaulted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"configureDependencies","outputs":[{"internalType":"Keycode[]","name":"dependencies","type":"bytes5[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dai","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"defund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Cooler","name":"cooler_","type":"address"},{"internalType":"uint256","name":"loanID_","type":"uint256"},{"internalType":"uint8","name":"times_","type":"uint8"}],"name":"extendLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract CoolerFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"principal_","type":"uint256"}],"name":"getCollateralForLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"collateral_","type":"uint256"}],"name":"getLoanForCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getTotalReceivables","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gohm","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"duration_","type":"uint256"}],"name":"interestForLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"interestReceivables","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isCoolerCallback","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"kernel","outputs":[{"internalType":"contract Kernel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Cooler","name":"cooler_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"lendToCooler","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ohm","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanID_","type":"uint256"},{"internalType":"uint256","name":"principle","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"collateral","type":"uint256"}],"name":"onDefault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanID_","type":"uint256"},{"internalType":"uint256","name":"principlePaid_","type":"uint256"},{"internalType":"uint256","name":"interestPaid_","type":"uint256"}],"name":"onRepay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"principalReceivables","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reactivate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rebalance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestPermissions","outputs":[{"components":[{"internalType":"Keycode","name":"keycode","type":"bytes5"},{"internalType":"bytes4","name":"funcSelector","type":"bytes4"}],"internalType":"struct Permissions[]","name":"requests","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sdai","outputs":[{"internalType":"contract ERC4626","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"staking","outputs":[{"internalType":"contract IStaking","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sweepIntoDSR","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101406040523480156200001257600080fd5b506040516200365038038062003650833981016040819052620000359162000121565b600080546001600160a01b0319166001600160a01b03838116919091179091558281166080528681166101005285811660e05284811661012052831660c0819052604080516338d52e0f60e01b815290516338d52e0f916004808201926020929091908290030181865afa158015620000b2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000d89190620001b5565b6001600160a01b031660a05250506003805460ff60a01b1916600160a01b17905550504260045550620001dc9050565b6001600160a01b03811681146200011e57600080fd5b50565b60008060008060008060c087890312156200013b57600080fd5b8651620001488162000108565b60208801519096506200015b8162000108565b60408801519095506200016e8162000108565b6060880151909450620001818162000108565b6080880151909350620001948162000108565b60a0880151909250620001a78162000108565b809150509295509295509295565b600060208284031215620001c857600080fd5b8151620001d58162000108565b9392505050565b60805160a05160c05160e05161010051610120516132db6200037560003960008181610346015281816111a101526112500152600081816101fe0152612337015260008181610256015281816105ba015281816107a001528181610872015281816110e40152818161117401528181611a6601526124430152600081816103a5015281816109a601528181610f3201528181610fb501528181611c3801528181611e0501528181611ea401528181611f2a01528181612080015281816121110152818161273c015281816127c1015281816128410152818161294f01526129a20152600081816104e10152818161065701528181610a3301528181610ce901528181610db001528181610ff40152818161107701528181611948015281816119ca01528181611b9701528181611cdc01528181611d7401528181611fe1015281816125710152818161270f0152818161287c015281816128de01528181612a2c0152612c3101526000818161047b0152818161052501528181610c3201528181611380015281816115d001526124b901526132db6000f3fe608060405234801561001057600080fd5b50600436106101d85760003560e01c8063017f1f44146101dd57806302b1d239146101f957806302fb0c5e1461022d5780630690b59e1461025157806309cfa2ee146102785780630d88a507146102815780630e1505e0146102945780631accd130146102a35780631be05289146102b557806322f3e2d4146102bf57806323831b77146102c75780632a471e25146102dc57806330743257146102e35780633403c2fc1461030b57806344df8e70146103135780634657b36c1461031b5780634b84126a1461032e5780634cf088d9146103415780634db71fc914610368578063577de7d0146103785780635924be701461038b5780635b2eeca4146103a05780635b72a33a146103c75780636cab0074146103d557806379904e4c146103e85780637b30f405146103fb5780637d7c2a1c1461040557806380cee4ab1461040d578063923cb952146104205780639459b87514610433578063a56a7f5914610448578063b3c6326b14610450578063b6278e8114610463578063c45a015514610476578063c51d85361461049d578063d18611d6146104a5578063d4aae0c4146104ad578063e37d16f8146104c0578063e7cc298e146104d3578063f4b9fa75146104dc575b600080fd5b6101e660055481565b6040519081526020015b60405180910390f35b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6040516101f09190612ca1565b60035461024190600160a01b900460ff1681565b60405190151581526020016101f0565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6101e660065481565b6101e661028f366004612cca565b610503565b6101e667016345785d8a000081565b6101e66a0ee3a5f48a68b55200000081565b6101e6629f858081565b610241610b34565b6102da6102d5366004612cf6565b610bab565b005b6001610241565b6102f66102f1366004612d3e565b610e4b565b604080519283526020830191909152016101f0565b6102da610e92565b6102da6110ca565b6102da610329366004612d57565b611311565b6102da61033c366004612d7b565b611369565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6101e6689cd35474a7270c000081565b600354610220906001600160a01b031681565b610393611419565b6040516101f09190612dad565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6101e66611c37937e0800081565b6102da6103e3366004612e5b565b61159c565b6101e66103f6366004612d3e565b611af7565b6101e662093a8081565b610241611b25565b600254610220906001600160a01b031681565b600154610220906001600160a01b031681565b61043b6121c3565b6040516101f09190612ec6565b6101e66123b9565b6102da61045e366004612cca565b6123cb565b6102da610471366004612f14565b6124a2565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b6102da612557565b6102da6125f5565b600054610220906001600160a01b031681565b6101e66104ce366004612f40565b6126ae565b6101e660045481565b6102207f000000000000000000000000000000000000000000000000000000000000000081565b600061050d611b25565b5060405163d42efd8360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd839061055a908690600401612ca1565b602060405180830381865afa158015610577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059b9190612f77565b6105b857604051636f2c0c0160e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610620573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106449190612f92565b6001600160a01b03161415806106ed57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316630dca59c16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e19190612f92565b6001600160a01b031614155b1561070b5760405163551febe960e01b815260040160405180910390fd5b6040516399c6b2ef60e01b815260048101839052689cd35474a7270c000060248201526000906001600160a01b038516906399c6b2ef90604401602060405180830381865afa158015610762573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107869190612faf565b6040516323b872dd60e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906107d990339030908690600401612fc8565b6020604051808303816000875af11580156107f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081c9190612f77565b50600061082882610e4b565b915050806005600082825461083d9190613002565b9250508190555083600660008282546108569190613002565b909155505060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906108a9908890869060040161301a565b6020604051808303816000875af11580156108c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ec9190612f77565b5060405163753c7ff960e01b8152600481018590526611c37937e080006024820152689cd35474a7270c00006044820152629f858060648201526000906001600160a01b0387169063753c7ff9906084016020604051808303816000875af115801561095c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109809190612faf565b604051632d182be560e21b815260048101879052306024820181905260448201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b460af94906064016020604051808303816000875af11580156109f7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1b9190612faf565b5060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b390610a6a908990899060040161301a565b6020604051808303816000875af1158015610a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aad9190612f77565b506040516310cf5a1960e01b815260048101829052306024820152600160448201526000906001600160a01b038816906310cf5a19906064016020604051808303816000875af1158015610b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b299190612faf565b979650505050505050565b6000805460405163e52223bb60e01b81526001600160a01b039091169063e52223bb90610b65903090600401612ca1565b602060405180830381865afa158015610b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba69190612f77565b905090565b604051632820036560e11b8152600481018390526000906001600160a01b0385169063504006ca906024016101a060405180830381865afa158015610bf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c18919061310d565b60405163d42efd8360e01b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd8390610c67908790600401612ca1565b602060405180830381865afa158015610c84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca89190612f77565b610cc557604051636f2c0c0160e01b815260040160405180910390fd5b6000610cdd82602001518360000151606001516126ae565b90506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166323b872dd3330610d1d60ff88168661319b565b6040518463ffffffff1660e01b8152600401610d3b93929190612fc8565b6020604051808303816000875af1158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e9190612f77565b50600354600160a01b900460ff1615610dab57610da6610da160ff85168361319b565b6126f8565b610de1565b610de17f0000000000000000000000000000000000000000000000000000000000000000610ddc60ff86168461319b565b61283f565b6040516327f7150360e21b81526004810185905260ff841660248201526001600160a01b03861690639fdc540c90604401600060405180830381600087803b158015610e2c57600080fd5b505af1158015610e40573d6000803e3d6000fd5b505050505050505050565b60008080670de0b6b3a7640000610e6b689cd35474a7270c00008661319b565b610e7591906131ba565b90506000610e8682629f85806126ae565b91959194509092505050565b60015460405163d09a20c560e01b81527132b6b2b933b2b731bcafb9b43aba3237bbb760711b916001600160a01b03169063d09a20c590610ed990849033906004016131dc565b600060405180830381600087803b158015610ef357600080fd5b505af1158015610f07573d6000803e3d6000fd5b50506003805460ff60a01b1916905550506040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190610f67903090600401612ca1565b602060405180830381865afa158015610f84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa89190612faf565b90508015610fda57610fda7f00000000000000000000000000000000000000000000000000000000000000008261283f565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190611029903090600401612ca1565b602060405180830381865afa158015611046573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106a9190612faf565b9050801561109c5761109c7f00000000000000000000000000000000000000000000000000000000000000008261283f565b6040517fc2a8834045efeaf0b37df1cf2e5979bff82a0c7f93c99b649a004940ef3cda4590600090a1505050565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190611119903090600401612ca1565b602060405180830381865afa158015611136573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115a9190612faf565b60405163095ea7b360e01b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906111cb907f000000000000000000000000000000000000000000000000000000000000000090859060040161301a565b6020604051808303816000875af11580156111ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120e9190612f77565b5060035460405163990966d560e01b815230600482018190526024820184905260006044830181905260648301526001600160a01b039283169263aaf0ad5a927f00000000000000000000000000000000000000000000000000000000000000009091169063990966d5906084016020604051808303816000875af115801561129b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bf9190612faf565b6040518363ffffffff1660e01b81526004016112dc92919061301a565b600060405180830381600087803b1580156112f657600080fd5b505af115801561130a573d6000803e3d6000fd5b5050505050565b6000546001600160a01b03163314611347573360405163053e900f60e21b815260040161133e9190612ca1565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60405163d42efd8360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd83906113b5903390600401612ca1565b602060405180830381865afa1580156113d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f69190612f77565b61141357604051636f2c0c0160e01b815260040160405180910390fd5b50505050565b60408051600480825260a0820190925260609164545253525960d81b9190602082015b604080518082019091526000808252602082015281526020019060019003908161143c575050604080518082019091526001600160d81b031983168152634aae164b60e01b6020820152815191935090839060009061149d5761149d6131f3565b60200260200101819052506040518060400160405280826001600160d81b031916815260200163e6eb207a60e01b6001600160e01b031916815250826001815181106114eb576114eb6131f3565b60200260200101819052506040518060400160405280826001600160d81b0319168152602001632f42aef560e01b6001600160e01b03191681525082600281518110611539576115396131f3565b6020026020010181905250604051806040016040528061155e6426a4a72a2960d91b90565b6001600160d81b031916815263557856ad60e11b60209091015282518390600390811061158d5761158d6131f3565b60200260200101819052505090565b808381146115bd57604051631700bfef60e01b815260040160405180910390fd5b60008060008060005b858110156118dc577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d42efd838b8b8481811061160f5761160f6131f3565b90506020020160208101906116249190612d57565b6040518263ffffffff1660e01b81526004016116409190612ca1565b602060405180830381865afa15801561165d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116819190612f77565b61169e57604051636f2c0c0160e01b815260040160405180910390fd5b308a8a838181106116b1576116b16131f3565b90506020020160208101906116c69190612d57565b6001600160a01b031663504006ca8a8a858181106116e6576116e66131f3565b905060200201356040518263ffffffff1660e01b815260040161170b91815260200190565b6101a060405180830381865afa158015611729573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174d919061310d565b60a001516001600160a01b03161461177857604051638c38000360e01b815260040160405180910390fd5b6000806000808d8d86818110611790576117906131f3565b90506020020160208101906117a59190612d57565b6001600160a01b03166357e8a2f08d8d888181106117c5576117c56131f3565b905060200201356040518263ffffffff1660e01b81526004016117ea91815260200190565b6080604051808303816000875af1158015611809573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182d9190613209565b9b83019b9a82019a99810199600190980197929650909450925090506000670de0b6b3a76400006118658466b1a2bc2ec5000061319b565b61186f91906131ba565b9050600067016345785d8a000082106118905767016345785d8a0000611892565b815b905062093a8083106118ad576118a88189613002565b6118cf565b62093a806118bb848361319b565b6118c591906131ba565b6118cf9089613002565b97505050505050506115c6565b5082600554116118ed5760006118fb565b826005546118fb919061323f565b600555600654841061190e57600061191c565b8360065461191c919061323f565b6006556002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace590611972907f0000000000000000000000000000000000000000000000000000000000000000903090600401613256565b602060405180830381865afa15801561198f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b39190612faf565b6002549091506001600160a01b0316634aae164b307f00000000000000000000000000000000000000000000000000000000000000008885116119f7576000611a01565b611a01898661323f565b6040518463ffffffff1660e01b8152600401611a1f93929190612fc8565b600060405180830381600087803b158015611a3957600080fd5b505af1158015611a4d573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016925063a9059cbb9150611a9f903390869060040161301a565b6020604051808303816000875af1158015611abe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ae29190612f77565b50611aeb6110ca565b50505050505050505050565b6000689cd35474a7270c0000611b1583670de0b6b3a764000061319b565b611b1f91906131ba565b92915050565b6003546000908190600160a01b900460ff16611b42576000611b4f565b6a0ee3a5f48a68b5520000005b9050426004541115611b6357600091505090565b62093a8060046000828254611b789190613002565b90915550506040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190611bcc903090600401612ca1565b602060405180830381865afa158015611be9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c0d9190612faf565b90508015611c1e57611c1e816126f8565b60405163ce96cb7760e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063ce96cb7790611c6d903090600401612ca1565b602060405180830381865afa158015611c8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cae9190612faf565b6002546040516328a4ace560e01b81529192506000916001600160a01b03909116906328a4ace590611d06907f0000000000000000000000000000000000000000000000000000000000000000903090600401613256565b602060405180830381865afa158015611d23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d479190612faf565b905083821015611fb6576000611d5d838661323f565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000000000000000000000000000000000000000000000611d9d8587613002565b6040518463ffffffff1660e01b8152600401611dbb93929190612fc8565b600060405180830381600087803b158015611dd557600080fd5b505af1158015611de9573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169150630a28a47790602401602060405180830381865afa158015611e55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e799190612faf565b600254604051637375903d60e11b81529192506001600160a01b03169063e6eb207a90611ece9030907f0000000000000000000000000000000000000000000000000000000000000000908690600401612fc8565b600060405180830381600087803b158015611ee857600080fd5b505af1158015611efc573d6000803e3d6000fd5b5050600254604051632f42aef560e01b81526001600160a01b039091169250632f42aef59150611f549030907f0000000000000000000000000000000000000000000000000000000000000000908690600401612fc8565b600060405180830381600087803b158015611f6e57600080fd5b505af1158015611f82573d6000803e3d6000fd5b5050604080516000815260208101869052600080516020613286833981519152935001905060405180910390a150506121b8565b838211156121b8576000611fca858461323f565b6002549091506001600160a01b0316634aae164b307f000000000000000000000000000000000000000000000000000000000000000084861161200e576000612018565b612018858761323f565b6040518463ffffffff1660e01b815260040161203693929190612fc8565b600060405180830381600087803b15801561205057600080fd5b505af1158015612064573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169150630a28a47790602401602060405180830381865afa1580156120d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f49190612faf565b60025460405163a9059cbb60e01b81529192506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169263a9059cbb92612149921690859060040161301a565b6020604051808303816000875af1158015612168573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218c9190612f77565b50604080516001815260208101849052600080516020613286833981519152910160405180910390a150505b600194505050505090565b6040805160038082526080820190925260609160208201838036833701905050905064545253525960d81b81600081518110612201576122016131f3565b6001600160d81b0319909216602092830291909101909101526122296426a4a72a2960d91b90565b8160018151811061223c5761223c6131f3565b6001600160d81b03199092166020928302919091019091015261226464524f4c455360d81b90565b81600281518110612277576122776131f3565b6001600160d81b0319909216602092830291909101909101526122a76122a264545253525960d81b90565b612b6b565b600280546001600160a01b0319166001600160a01b03929092169190911790556122d86426a4a72a2960d91b612b6b565b600380546001600160a01b0319166001600160a01b039290921691909117905561230964524f4c455360d81b612b6b565b600180546001600160a01b0319166001600160a01b0392831617905560035460405163095ea7b360e01b81527f000000000000000000000000000000000000000000000000000000000000000083169263095ea7b392612372929116906000199060040161301a565b6020604051808303816000875af1158015612391573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123b59190612f77565b5090565b6000600554600654610ba69190613002565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c59061240f90849033906004016131dc565b600060405180830381600087803b15801561242957600080fd5b505af115801561243d573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031603612493576040516323286f0960e01b815260040160405180910390fd5b61249d838361283f565b505050565b60405163d42efd8360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d42efd83906124ee903390600401612ca1565b602060405180830381865afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190612f77565b61254c57604051636f2c0c0160e01b815260040160405180910390fd5b61249d838383612c08565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a08231906125a6903090600401612ca1565b602060405180830381865afa1580156125c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e79190612faf565b90506125f2816126f8565b50565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c59061263990849033906004016131dc565b600060405180830381600087803b15801561265357600080fd5b505af1158015612667573d6000803e3d6000fd5b50506003805460ff60a01b1916600160a01b1790555050426004556040517fff9b636475d62fa647c909374b687183b15b7c2166488c6ab7ff0e7f850584c890600090a150565b6000806301e133806126c7846611c37937e0800061319b565b6126d191906131ba565b9050670de0b6b3a76400006126e6828661319b565b6126f091906131ba565b949350505050565b60405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b390612766907f000000000000000000000000000000000000000000000000000000000000000090859060040161301a565b6020604051808303816000875af1158015612785573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127a99190612f77565b50604051636e553f6560e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636e553f65906127f890849030906004016131dc565b6020604051808303816000875af1158015612817573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283b9190612faf565b5050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031614806128b057507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316145b15612ab6576002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace590612908907f0000000000000000000000000000000000000000000000000000000000000000903090600401613256565b602060405180830381865afa158015612925573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129499190612faf565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b03161461298c5782612a15565b60405163266d6a8360e11b8152600481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690634cdad50690602401602060405180830381865afa1580156129f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a159190612faf565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000000000000000000000000000000000000000000000848611612a59576000612a63565b612a63858761323f565b6040518463ffffffff1660e01b8152600401612a8193929190612fc8565b600060405180830381600087803b158015612a9b57600080fd5b505af1158015612aaf573d6000803e3d6000fd5b5050505050505b60025460405163a9059cbb60e01b81526001600160a01b038481169263a9059cbb92612aea9290911690859060040161301a565b6020604051808303816000875af1158015612b09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b2d9190612f77565b507f8cbbc3aaf474846c72611023689674ac355cc59309151e8137ffac41a0abde2f8282604051612b5f92919061301a565b60405180910390a15050565b60008054604051632d37002d60e21b815282916001600160a01b03169063b4dc00b490612b9c908690600401613270565b602060405180830381865afa158015612bb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bdd9190612f92565b90506001600160a01b038116611b1f5782604051635c3fa9cd60e01b815260040161133e9190613270565b600354600160a01b900460ff1615612c2c57612c27610da18284613002565b612c5a565b612c5a7f0000000000000000000000000000000000000000000000000000000000000000610ddc8385613002565b8060055411612c6a576000612c78565b80600554612c78919061323f565b6005556006548210612c8b576000612c99565b81600654612c99919061323f565b600655505050565b6001600160a01b0391909116815260200190565b6001600160a01b03811681146125f257600080fd5b60008060408385031215612cdd57600080fd5b8235612ce881612cb5565b946020939093013593505050565b600080600060608486031215612d0b57600080fd5b8335612d1681612cb5565b925060208401359150604084013560ff81168114612d3357600080fd5b809150509250925092565b600060208284031215612d5057600080fd5b5035919050565b600060208284031215612d6957600080fd5b8135612d7481612cb5565b9392505050565b60008060008060808587031215612d9157600080fd5b5050823594602084013594506040840135936060013592509050565b602080825282518282018190526000919060409081850190868401855b82811015612e0357815180516001600160d81b03191685528601516001600160e01b031916868501529284019290850190600101612dca565b5091979650505050505050565b60008083601f840112612e2257600080fd5b5081356001600160401b03811115612e3957600080fd5b6020830191508360208260051b8501011115612e5457600080fd5b9250929050565b60008060008060408587031215612e7157600080fd5b84356001600160401b0380821115612e8857600080fd5b612e9488838901612e10565b90965094506020870135915080821115612ead57600080fd5b50612eba87828801612e10565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015612f085783516001600160d81b03191683529284019291840191600101612ee2565b50909695505050505050565b600080600060608486031215612f2957600080fd5b505081359360208301359350604090920135919050565b60008060408385031215612f5357600080fd5b50508035926020909101359150565b80518015158114612f7257600080fd5b919050565b600060208284031215612f8957600080fd5b612d7482612f62565b600060208284031215612fa457600080fd5b8151612d7481612cb5565b600060208284031215612fc157600080fd5b5051919050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052601160045260246000fd5b6000821982111561301557613015612fec565b500190565b6001600160a01b03929092168252602082015260400190565b60405161010081016001600160401b038111828210171561306457634e487b7160e01b600052604160045260246000fd5b60405290565b8051612f7281612cb5565b600060c0828403121561308757600080fd5b60405160c081016001600160401b03811182821017156130b757634e487b7160e01b600052604160045260246000fd5b8060405250809150825181526020830151602082015260408301516040820152606083015160608201526130ed60808401612f62565b608082015260a083015161310081612cb5565b60a0919091015292915050565b60006101a0828403121561312057600080fd5b613128613033565b6131328484613075565b815260c0830151602082015260e083015160408201526101008301516060820152610120830151608082015261316b610140840161306a565b60a082015261317d610160840161306a565b60c082015261318f6101808401612f62565b60e08201529392505050565b60008160001904831182151516156131b5576131b5612fec565b500290565b6000826131d757634e487b7160e01b600052601260045260246000fd5b500490565b9182526001600160a01b0316602082015260400190565b634e487b7160e01b600052603260045260246000fd5b6000806000806080858703121561321f57600080fd5b505082516020840151604085015160609095015191969095509092509050565b60008282101561325157613251612fec565b500390565b6001600160a01b0392831681529116602082015260400190565b6001600160d81b03199190911681526020019056fe533dcda197d26e3a7e25715288a04e3923e2fc9609017f5935abbfc9249e6199a2646970667358221220d205e3efb2fe0a5ca57840cf78a349373f74cd12b83978ac9eaa6aa0a2a22e4b64736f6c634300080f003300000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d50000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f000000000000000000000000b63cac384247597756545b500253ff8e607a802000000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db42160000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101d85760003560e01c8063017f1f44146101dd57806302b1d239146101f957806302fb0c5e1461022d5780630690b59e1461025157806309cfa2ee146102785780630d88a507146102815780630e1505e0146102945780631accd130146102a35780631be05289146102b557806322f3e2d4146102bf57806323831b77146102c75780632a471e25146102dc57806330743257146102e35780633403c2fc1461030b57806344df8e70146103135780634657b36c1461031b5780634b84126a1461032e5780634cf088d9146103415780634db71fc914610368578063577de7d0146103785780635924be701461038b5780635b2eeca4146103a05780635b72a33a146103c75780636cab0074146103d557806379904e4c146103e85780637b30f405146103fb5780637d7c2a1c1461040557806380cee4ab1461040d578063923cb952146104205780639459b87514610433578063a56a7f5914610448578063b3c6326b14610450578063b6278e8114610463578063c45a015514610476578063c51d85361461049d578063d18611d6146104a5578063d4aae0c4146104ad578063e37d16f8146104c0578063e7cc298e146104d3578063f4b9fa75146104dc575b600080fd5b6101e660055481565b6040519081526020015b60405180910390f35b6102207f00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d581565b6040516101f09190612ca1565b60035461024190600160a01b900460ff1681565b60405190151581526020016101f0565b6102207f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f81565b6101e660065481565b6101e661028f366004612cca565b610503565b6101e667016345785d8a000081565b6101e66a0ee3a5f48a68b55200000081565b6101e6629f858081565b610241610b34565b6102da6102d5366004612cf6565b610bab565b005b6001610241565b6102f66102f1366004612d3e565b610e4b565b604080519283526020830191909152016101f0565b6102da610e92565b6102da6110ca565b6102da610329366004612d57565b611311565b6102da61033c366004612d7b565b611369565b6102207f000000000000000000000000b63cac384247597756545b500253ff8e607a802081565b6101e6689cd35474a7270c000081565b600354610220906001600160a01b031681565b610393611419565b6040516101f09190612dad565b6102207f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea81565b6101e66611c37937e0800081565b6102da6103e3366004612e5b565b61159c565b6101e66103f6366004612d3e565b611af7565b6101e662093a8081565b610241611b25565b600254610220906001600160a01b031681565b600154610220906001600160a01b031681565b61043b6121c3565b6040516101f09190612ec6565b6101e66123b9565b6102da61045e366004612cca565b6123cb565b6102da610471366004612f14565b6124a2565b6102207f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db421681565b6102da612557565b6102da6125f5565b600054610220906001600160a01b031681565b6101e66104ce366004612f40565b6126ae565b6101e660045481565b6102207f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f81565b600061050d611b25565b5060405163d42efd8360e01b81526001600160a01b037f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db4216169063d42efd839061055a908690600401612ca1565b602060405180830381865afa158015610577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059b9190612f77565b6105b857604051636f2c0c0160e01b815260040160405180910390fd5b7f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f6001600160a01b0316836001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610620573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106449190612f92565b6001600160a01b03161415806106ed57507f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b0316836001600160a01b0316630dca59c16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e19190612f92565b6001600160a01b031614155b1561070b5760405163551febe960e01b815260040160405180910390fd5b6040516399c6b2ef60e01b815260048101839052689cd35474a7270c000060248201526000906001600160a01b038516906399c6b2ef90604401602060405180830381865afa158015610762573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107869190612faf565b6040516323b872dd60e01b81529091506001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f16906323b872dd906107d990339030908690600401612fc8565b6020604051808303816000875af11580156107f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081c9190612f77565b50600061082882610e4b565b915050806005600082825461083d9190613002565b9250508190555083600660008282546108569190613002565b909155505060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f169063095ea7b3906108a9908890869060040161301a565b6020604051808303816000875af11580156108c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ec9190612f77565b5060405163753c7ff960e01b8152600481018590526611c37937e080006024820152689cd35474a7270c00006044820152629f858060648201526000906001600160a01b0387169063753c7ff9906084016020604051808303816000875af115801561095c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109809190612faf565b604051632d182be560e21b815260048101879052306024820181905260448201529091507f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b03169063b460af94906064016020604051808303816000875af11580156109f7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1b9190612faf565b5060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f169063095ea7b390610a6a908990899060040161301a565b6020604051808303816000875af1158015610a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aad9190612f77565b506040516310cf5a1960e01b815260048101829052306024820152600160448201526000906001600160a01b038816906310cf5a19906064016020604051808303816000875af1158015610b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b299190612faf565b979650505050505050565b6000805460405163e52223bb60e01b81526001600160a01b039091169063e52223bb90610b65903090600401612ca1565b602060405180830381865afa158015610b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba69190612f77565b905090565b604051632820036560e11b8152600481018390526000906001600160a01b0385169063504006ca906024016101a060405180830381865afa158015610bf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c18919061310d565b60405163d42efd8360e01b81529091506001600160a01b037f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db4216169063d42efd8390610c67908790600401612ca1565b602060405180830381865afa158015610c84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca89190612f77565b610cc557604051636f2c0c0160e01b815260040160405180910390fd5b6000610cdd82602001518360000151606001516126ae565b90506001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f166323b872dd3330610d1d60ff88168661319b565b6040518463ffffffff1660e01b8152600401610d3b93929190612fc8565b6020604051808303816000875af1158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e9190612f77565b50600354600160a01b900460ff1615610dab57610da6610da160ff85168361319b565b6126f8565b610de1565b610de17f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f610ddc60ff86168461319b565b61283f565b6040516327f7150360e21b81526004810185905260ff841660248201526001600160a01b03861690639fdc540c90604401600060405180830381600087803b158015610e2c57600080fd5b505af1158015610e40573d6000803e3d6000fd5b505050505050505050565b60008080670de0b6b3a7640000610e6b689cd35474a7270c00008661319b565b610e7591906131ba565b90506000610e8682629f85806126ae565b91959194509092505050565b60015460405163d09a20c560e01b81527132b6b2b933b2b731bcafb9b43aba3237bbb760711b916001600160a01b03169063d09a20c590610ed990849033906004016131dc565b600060405180830381600087803b158015610ef357600080fd5b505af1158015610f07573d6000803e3d6000fd5b50506003805460ff60a01b1916905550506040516370a0823160e01b81526000906001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea16906370a0823190610f67903090600401612ca1565b602060405180830381865afa158015610f84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa89190612faf565b90508015610fda57610fda7f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea8261283f565b6040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16906370a0823190611029903090600401612ca1565b602060405180830381865afa158015611046573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106a9190612faf565b9050801561109c5761109c7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f8261283f565b6040517fc2a8834045efeaf0b37df1cf2e5979bff82a0c7f93c99b649a004940ef3cda4590600090a1505050565b6040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f16906370a0823190611119903090600401612ca1565b602060405180830381865afa158015611136573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115a9190612faf565b60405163095ea7b360e01b81529091506001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f169063095ea7b3906111cb907f000000000000000000000000b63cac384247597756545b500253ff8e607a802090859060040161301a565b6020604051808303816000875af11580156111ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120e9190612f77565b5060035460405163990966d560e01b815230600482018190526024820184905260006044830181905260648301526001600160a01b039283169263aaf0ad5a927f000000000000000000000000b63cac384247597756545b500253ff8e607a80209091169063990966d5906084016020604051808303816000875af115801561129b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bf9190612faf565b6040518363ffffffff1660e01b81526004016112dc92919061301a565b600060405180830381600087803b1580156112f657600080fd5b505af115801561130a573d6000803e3d6000fd5b5050505050565b6000546001600160a01b03163314611347573360405163053e900f60e21b815260040161133e9190612ca1565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60405163d42efd8360e01b81526001600160a01b037f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db4216169063d42efd83906113b5903390600401612ca1565b602060405180830381865afa1580156113d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f69190612f77565b61141357604051636f2c0c0160e01b815260040160405180910390fd5b50505050565b60408051600480825260a0820190925260609164545253525960d81b9190602082015b604080518082019091526000808252602082015281526020019060019003908161143c575050604080518082019091526001600160d81b031983168152634aae164b60e01b6020820152815191935090839060009061149d5761149d6131f3565b60200260200101819052506040518060400160405280826001600160d81b031916815260200163e6eb207a60e01b6001600160e01b031916815250826001815181106114eb576114eb6131f3565b60200260200101819052506040518060400160405280826001600160d81b0319168152602001632f42aef560e01b6001600160e01b03191681525082600281518110611539576115396131f3565b6020026020010181905250604051806040016040528061155e6426a4a72a2960d91b90565b6001600160d81b031916815263557856ad60e11b60209091015282518390600390811061158d5761158d6131f3565b60200260200101819052505090565b808381146115bd57604051631700bfef60e01b815260040160405180910390fd5b60008060008060005b858110156118dc577f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db42166001600160a01b031663d42efd838b8b8481811061160f5761160f6131f3565b90506020020160208101906116249190612d57565b6040518263ffffffff1660e01b81526004016116409190612ca1565b602060405180830381865afa15801561165d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116819190612f77565b61169e57604051636f2c0c0160e01b815260040160405180910390fd5b308a8a838181106116b1576116b16131f3565b90506020020160208101906116c69190612d57565b6001600160a01b031663504006ca8a8a858181106116e6576116e66131f3565b905060200201356040518263ffffffff1660e01b815260040161170b91815260200190565b6101a060405180830381865afa158015611729573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174d919061310d565b60a001516001600160a01b03161461177857604051638c38000360e01b815260040160405180910390fd5b6000806000808d8d86818110611790576117906131f3565b90506020020160208101906117a59190612d57565b6001600160a01b03166357e8a2f08d8d888181106117c5576117c56131f3565b905060200201356040518263ffffffff1660e01b81526004016117ea91815260200190565b6080604051808303816000875af1158015611809573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182d9190613209565b9b83019b9a82019a99810199600190980197929650909450925090506000670de0b6b3a76400006118658466b1a2bc2ec5000061319b565b61186f91906131ba565b9050600067016345785d8a000082106118905767016345785d8a0000611892565b815b905062093a8083106118ad576118a88189613002565b6118cf565b62093a806118bb848361319b565b6118c591906131ba565b6118cf9089613002565b97505050505050506115c6565b5082600554116118ed5760006118fb565b826005546118fb919061323f565b600555600654841061190e57600061191c565b8360065461191c919061323f565b6006556002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace590611972907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f903090600401613256565b602060405180830381865afa15801561198f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b39190612faf565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f8885116119f7576000611a01565b611a01898661323f565b6040518463ffffffff1660e01b8152600401611a1f93929190612fc8565b600060405180830381600087803b158015611a3957600080fd5b505af1158015611a4d573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f16925063a9059cbb9150611a9f903390869060040161301a565b6020604051808303816000875af1158015611abe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ae29190612f77565b50611aeb6110ca565b50505050505050505050565b6000689cd35474a7270c0000611b1583670de0b6b3a764000061319b565b611b1f91906131ba565b92915050565b6003546000908190600160a01b900460ff16611b42576000611b4f565b6a0ee3a5f48a68b5520000005b9050426004541115611b6357600091505090565b62093a8060046000828254611b789190613002565b90915550506040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16906370a0823190611bcc903090600401612ca1565b602060405180830381865afa158015611be9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c0d9190612faf565b90508015611c1e57611c1e816126f8565b60405163ce96cb7760e01b81526000906001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea169063ce96cb7790611c6d903090600401612ca1565b602060405180830381865afa158015611c8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cae9190612faf565b6002546040516328a4ace560e01b81529192506000916001600160a01b03909116906328a4ace590611d06907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f903090600401613256565b602060405180830381865afa158015611d23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d479190612faf565b905083821015611fb6576000611d5d838661323f565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f611d9d8587613002565b6040518463ffffffff1660e01b8152600401611dbb93929190612fc8565b600060405180830381600087803b158015611dd557600080fd5b505af1158015611de9573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b03169150630a28a47790602401602060405180830381865afa158015611e55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e799190612faf565b600254604051637375903d60e11b81529192506001600160a01b03169063e6eb207a90611ece9030907f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea908690600401612fc8565b600060405180830381600087803b158015611ee857600080fd5b505af1158015611efc573d6000803e3d6000fd5b5050600254604051632f42aef560e01b81526001600160a01b039091169250632f42aef59150611f549030907f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea908690600401612fc8565b600060405180830381600087803b158015611f6e57600080fd5b505af1158015611f82573d6000803e3d6000fd5b5050604080516000815260208101869052600080516020613286833981519152935001905060405180910390a150506121b8565b838211156121b8576000611fca858461323f565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f84861161200e576000612018565b612018858761323f565b6040518463ffffffff1660e01b815260040161203693929190612fc8565b600060405180830381600087803b15801561205057600080fd5b505af1158015612064573d6000803e3d6000fd5b5050604051630a28a47760e01b815260048101849052600092507f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b03169150630a28a47790602401602060405180830381865afa1580156120d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f49190612faf565b60025460405163a9059cbb60e01b81529192506001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea81169263a9059cbb92612149921690859060040161301a565b6020604051808303816000875af1158015612168573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218c9190612f77565b50604080516001815260208101849052600080516020613286833981519152910160405180910390a150505b600194505050505090565b6040805160038082526080820190925260609160208201838036833701905050905064545253525960d81b81600081518110612201576122016131f3565b6001600160d81b0319909216602092830291909101909101526122296426a4a72a2960d91b90565b8160018151811061223c5761223c6131f3565b6001600160d81b03199092166020928302919091019091015261226464524f4c455360d81b90565b81600281518110612277576122776131f3565b6001600160d81b0319909216602092830291909101909101526122a76122a264545253525960d81b90565b612b6b565b600280546001600160a01b0319166001600160a01b03929092169190911790556122d86426a4a72a2960d91b612b6b565b600380546001600160a01b0319166001600160a01b039290921691909117905561230964524f4c455360d81b612b6b565b600180546001600160a01b0319166001600160a01b0392831617905560035460405163095ea7b360e01b81527f00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d583169263095ea7b392612372929116906000199060040161301a565b6020604051808303816000875af1158015612391573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123b59190612f77565b5090565b6000600554600654610ba69190613002565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c59061240f90849033906004016131dc565b600060405180830381600087803b15801561242957600080fd5b505af115801561243d573d6000803e3d6000fd5b505050507f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f6001600160a01b0316836001600160a01b031603612493576040516323286f0960e01b815260040160405180910390fd5b61249d838361283f565b505050565b60405163d42efd8360e01b81526001600160a01b037f00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db4216169063d42efd83906124ee903390600401612ca1565b602060405180830381865afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190612f77565b61254c57604051636f2c0c0160e01b815260040160405180910390fd5b61249d838383612c08565b6040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16906370a08231906125a6903090600401612ca1565b602060405180830381865afa1580156125c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e79190612faf565b90506125f2816126f8565b50565b60015460405163d09a20c560e01b81526e31b7b7b632b92fb7bb32b939b2b2b960891b916001600160a01b03169063d09a20c59061263990849033906004016131dc565b600060405180830381600087803b15801561265357600080fd5b505af1158015612667573d6000803e3d6000fd5b50506003805460ff60a01b1916600160a01b1790555050426004556040517fff9b636475d62fa647c909374b687183b15b7c2166488c6ab7ff0e7f850584c890600090a150565b6000806301e133806126c7846611c37937e0800061319b565b6126d191906131ba565b9050670de0b6b3a76400006126e6828661319b565b6126f091906131ba565b949350505050565b60405163095ea7b360e01b81526001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f169063095ea7b390612766907f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea90859060040161301a565b6020604051808303816000875af1158015612785573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127a99190612f77565b50604051636e553f6560e01b81526001600160a01b037f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea1690636e553f65906127f890849030906004016131dc565b6020604051808303816000875af1158015612817573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283b9190612faf565b5050565b7f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b0316826001600160a01b031614806128b057507f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b0316826001600160a01b0316145b15612ab6576002546040516328a4ace560e01b81526000916001600160a01b0316906328a4ace590612908907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f903090600401613256565b602060405180830381865afa158015612925573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129499190612faf565b905060007f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b0316846001600160a01b03161461298c5782612a15565b60405163266d6a8360e11b8152600481018490527f00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea6001600160a01b031690634cdad50690602401602060405180830381865afa1580156129f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a159190612faf565b6002549091506001600160a01b0316634aae164b307f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f848611612a59576000612a63565b612a63858761323f565b6040518463ffffffff1660e01b8152600401612a8193929190612fc8565b600060405180830381600087803b158015612a9b57600080fd5b505af1158015612aaf573d6000803e3d6000fd5b5050505050505b60025460405163a9059cbb60e01b81526001600160a01b038481169263a9059cbb92612aea9290911690859060040161301a565b6020604051808303816000875af1158015612b09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b2d9190612f77565b507f8cbbc3aaf474846c72611023689674ac355cc59309151e8137ffac41a0abde2f8282604051612b5f92919061301a565b60405180910390a15050565b60008054604051632d37002d60e21b815282916001600160a01b03169063b4dc00b490612b9c908690600401613270565b602060405180830381865afa158015612bb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bdd9190612f92565b90506001600160a01b038116611b1f5782604051635c3fa9cd60e01b815260040161133e9190613270565b600354600160a01b900460ff1615612c2c57612c27610da18284613002565b612c5a565b612c5a7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f610ddc8385613002565b8060055411612c6a576000612c78565b80600554612c78919061323f565b6005556006548210612c8b576000612c99565b81600654612c99919061323f565b600655505050565b6001600160a01b0391909116815260200190565b6001600160a01b03811681146125f257600080fd5b60008060408385031215612cdd57600080fd5b8235612ce881612cb5565b946020939093013593505050565b600080600060608486031215612d0b57600080fd5b8335612d1681612cb5565b925060208401359150604084013560ff81168114612d3357600080fd5b809150509250925092565b600060208284031215612d5057600080fd5b5035919050565b600060208284031215612d6957600080fd5b8135612d7481612cb5565b9392505050565b60008060008060808587031215612d9157600080fd5b5050823594602084013594506040840135936060013592509050565b602080825282518282018190526000919060409081850190868401855b82811015612e0357815180516001600160d81b03191685528601516001600160e01b031916868501529284019290850190600101612dca565b5091979650505050505050565b60008083601f840112612e2257600080fd5b5081356001600160401b03811115612e3957600080fd5b6020830191508360208260051b8501011115612e5457600080fd5b9250929050565b60008060008060408587031215612e7157600080fd5b84356001600160401b0380821115612e8857600080fd5b612e9488838901612e10565b90965094506020870135915080821115612ead57600080fd5b50612eba87828801612e10565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015612f085783516001600160d81b03191683529284019291840191600101612ee2565b50909695505050505050565b600080600060608486031215612f2957600080fd5b505081359360208301359350604090920135919050565b60008060408385031215612f5357600080fd5b50508035926020909101359150565b80518015158114612f7257600080fd5b919050565b600060208284031215612f8957600080fd5b612d7482612f62565b600060208284031215612fa457600080fd5b8151612d7481612cb5565b600060208284031215612fc157600080fd5b5051919050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052601160045260246000fd5b6000821982111561301557613015612fec565b500190565b6001600160a01b03929092168252602082015260400190565b60405161010081016001600160401b038111828210171561306457634e487b7160e01b600052604160045260246000fd5b60405290565b8051612f7281612cb5565b600060c0828403121561308757600080fd5b60405160c081016001600160401b03811182821017156130b757634e487b7160e01b600052604160045260246000fd5b8060405250809150825181526020830151602082015260408301516040820152606083015160608201526130ed60808401612f62565b608082015260a083015161310081612cb5565b60a0919091015292915050565b60006101a0828403121561312057600080fd5b613128613033565b6131328484613075565b815260c0830151602082015260e083015160408201526101008301516060820152610120830151608082015261316b610140840161306a565b60a082015261317d610160840161306a565b60c082015261318f6101808401612f62565b60e08201529392505050565b60008160001904831182151516156131b5576131b5612fec565b500290565b6000826131d757634e487b7160e01b600052601260045260246000fd5b500490565b9182526001600160a01b0316602082015260400190565b634e487b7160e01b600052603260045260246000fd5b6000806000806080858703121561321f57600080fd5b505082516020840151604085015160609095015191969095509092509050565b60008282101561325157613251612fec565b500390565b6001600160a01b0392831681529116602082015260400190565b6001600160d81b03199190911681526020019056fe533dcda197d26e3a7e25715288a04e3923e2fc9609017f5935abbfc9249e6199a2646970667358221220d205e3efb2fe0a5ca57840cf78a349373f74cd12b83978ac9eaa6aa0a2a22e4b64736f6c634300080f0033

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

00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d50000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f000000000000000000000000b63cac384247597756545b500253ff8e607a802000000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db42160000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b

-----Decoded View---------------
Arg [0] : ohm_ (address): 0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5
Arg [1] : gohm_ (address): 0x0ab87046fBb341D058F17CBC4c1133F25a20a52f
Arg [2] : staking_ (address): 0xB63cac384247597756545b500253ff8E607a8020
Arg [3] : sdai_ (address): 0x83F20F44975D03b1b09e64809B757c47f942BEeA
Arg [4] : coolerFactory_ (address): 0x30Ce56e80aA96EbbA1E1a74bC5c0FEB5B0dB4216
Arg [5] : kernel_ (address): 0x2286d7f9639e8158FaD1169e76d1FbC38247f54b

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5
Arg [1] : 0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f
Arg [2] : 000000000000000000000000b63cac384247597756545b500253ff8e607a8020
Arg [3] : 00000000000000000000000083f20f44975d03b1b09e64809b757c47f942beea
Arg [4] : 00000000000000000000000030ce56e80aa96ebba1e1a74bc5c0feb5b0db4216
Arg [5] : 0000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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