ETH Price: $3,335.26 (-1.36%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MapleLoan

Compiler Version
v0.8.7+commit.e28d00a7

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 16 : MapleLoan.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { IERC20 }             from "../modules/erc20/contracts/interfaces/IERC20.sol";
import { ERC20Helper }        from "../modules/erc20-helper/src/ERC20Helper.sol";
import { IMapleProxyFactory } from "../modules/maple-proxy-factory/contracts/interfaces/IMapleProxyFactory.sol";

import { IMapleLoan }        from "./interfaces/IMapleLoan.sol";
import { IMapleGlobalsLike } from "./interfaces/Interfaces.sol";

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

/// @title MapleLoan implements a primitive loan with additional functionality, and is intended to be proxied.
contract MapleLoan is IMapleLoan, MapleLoanInternals {

    modifier whenProtocolNotPaused() {
        require(!isProtocolPaused(), "ML:PROTOCOL_PAUSED");
        _;
    }

    /********************************/
    /*** Administrative Functions ***/
    /********************************/

    function migrate(address migrator_, bytes calldata arguments_) external override {
        require(msg.sender == _factory(),        "ML:M:NOT_FACTORY");
        require(_migrate(migrator_, arguments_), "ML:M:FAILED");
    }

    function setImplementation(address newImplementation_) external override {
        require(msg.sender == _factory(),               "ML:SI:NOT_FACTORY");
        require(_setImplementation(newImplementation_), "ML:SI:FAILED");
    }

    function upgrade(uint256 toVersion_, bytes calldata arguments_) external override {
        require(msg.sender == IMapleGlobalsLike(_mapleGlobals()).globalAdmin(), "ML:U:NOT_ADMIN");

        emit Upgraded(toVersion_, arguments_);

        IMapleProxyFactory(_factory()).upgradeInstance(toVersion_, arguments_);
    }

    /************************/
    /*** Borrow Functions ***/
    /************************/

    function acceptBorrower() external override {
        require(msg.sender == _pendingBorrower, "ML:AB:NOT_PENDING_BORROWER");

        _pendingBorrower = address(0);

        emit BorrowerAccepted(_borrower = msg.sender);
    }

    function closeLoan(uint256 amount_) external override returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) {
        uint256 drawableFundsBeforePayment = _drawableFunds;

        // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs.
        require(amount_ == uint256(0) || ERC20Helper.transferFrom(_fundsAsset, msg.sender, address(this), amount_), "ML:CL:TRANSFER_FROM_FAILED");

        ( principal_, interest_, delegateFee_, treasuryFee_ ) = _closeLoan();

        // Either the caller is the borrower or `_drawableFunds` has not decreased.
        require(msg.sender == _borrower || _drawableFunds >= drawableFundsBeforePayment, "ML:CL:CANNOT_USE_DRAWABLE");

        emit LoanClosed(principal_, interest_, delegateFee_, treasuryFee_);
    }

    function drawdownFunds(uint256 amount_, address destination_) external override whenProtocolNotPaused returns (uint256 collateralPosted_) {
        require(msg.sender == _borrower, "ML:DF:NOT_BORROWER");

        emit FundsDrawnDown(amount_, destination_);

        // Post additional collateral required to facilitate this drawdown, if needed.
        uint256 additionalCollateralRequired = getAdditionalCollateralRequiredFor(amount_);

        if (additionalCollateralRequired > uint256(0)) {
            // Determine collateral currently unaccounted for.
            uint256 unaccountedCollateral = _getUnaccountedAmount(_collateralAsset);

            // Post required collateral, specifying then amount lacking as the optional amount to be transferred from.
            collateralPosted_ = postCollateral(
                additionalCollateralRequired > unaccountedCollateral ? additionalCollateralRequired - unaccountedCollateral : uint256(0)
            );
        }

        _drawdownFunds(amount_, destination_);
    }

    function makePayment(uint256 amount_) external override returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) {
        uint256 drawableFundsBeforePayment = _drawableFunds;

        // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs.
        require(amount_ == uint256(0) || ERC20Helper.transferFrom(_fundsAsset, msg.sender, address(this), amount_), "ML:MP:TRANSFER_FROM_FAILED");

        ( principal_, interest_, delegateFee_, treasuryFee_ ) = _makePayment();

        // Either the caller is the borrower or `_drawableFunds` has not decreased.
        require(msg.sender == _borrower || _drawableFunds >= drawableFundsBeforePayment, "ML:MP:CANNOT_USE_DRAWABLE");

        emit PaymentMade(principal_, interest_, delegateFee_, treasuryFee_);
    }

    function postCollateral(uint256 amount_) public override whenProtocolNotPaused returns (uint256 collateralPosted_) {
        // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs.
        require(
            amount_ == uint256(0) || ERC20Helper.transferFrom(_collateralAsset, msg.sender, address(this), amount_),
            "ML:PC:TRANSFER_FROM_FAILED"
        );

        emit CollateralPosted(collateralPosted_ = _postCollateral());
    }

    function proposeNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external override whenProtocolNotPaused {
        require(msg.sender == _borrower,      "ML:PNT:NOT_BORROWER");
        require(deadline_ >= block.timestamp, "ML:PNT:INVALID_DEADLINE");

        emit NewTermsProposed(_proposeNewTerms(refinancer_, deadline_, calls_), refinancer_, deadline_, calls_);
    }

    function removeCollateral(uint256 amount_, address destination_) external override whenProtocolNotPaused {
        require(msg.sender == _borrower, "ML:RC:NOT_BORROWER");

        emit CollateralRemoved(amount_, destination_);

        _removeCollateral(amount_, destination_);
    }

    function returnFunds(uint256 amount_) external override whenProtocolNotPaused returns (uint256 fundsReturned_) {
        // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs.
        require(amount_ == uint256(0) || ERC20Helper.transferFrom(_fundsAsset, msg.sender, address(this), amount_), "ML:RF:TRANSFER_FROM_FAILED");

        emit FundsReturned(fundsReturned_ = _returnFunds());
    }

    function setPendingBorrower(address pendingBorrower_) external override {
        require(msg.sender == _borrower, "ML:SPB:NOT_BORROWER");

        emit PendingBorrowerSet(_pendingBorrower = pendingBorrower_);
    }

    /**********************/
    /*** Lend Functions ***/
    /**********************/

    function acceptLender() external override {
        require(msg.sender == _pendingLender, "ML:AL:NOT_PENDING_LENDER");

        _pendingLender = address(0);

        emit LenderAccepted(_lender = msg.sender);
    }

    function acceptNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_, uint256 amount_) external override whenProtocolNotPaused {
        address lenderAddress = _lender;

        require(msg.sender == lenderAddress, "ML:ANT:NOT_LENDER");

        address fundsAssetAddress = _fundsAsset;

        // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs.
        require(amount_ == uint256(0) || ERC20Helper.transferFrom(fundsAssetAddress, msg.sender, address(this), amount_), "ML:ANT:TRANSFER_FROM_FAILED");

        emit NewTermsAccepted(_acceptNewTerms(refinancer_, deadline_, calls_), refinancer_, deadline_, calls_);
        emit EstablishmentFeesSet(_delegateFee, _treasuryFee);

        uint256 extra = _getUnaccountedAmount(fundsAssetAddress);

        // NOTE: This block ensures unaccounted funds (pre-existing or due to over-funding) gets redirected to the lender.
        if (extra > uint256(0)) {
            emit FundsRedirected(extra, lenderAddress);
            require(ERC20Helper.transfer(fundsAssetAddress, lenderAddress, extra), "ML:ANT:TRANSFER_FAILED");
        }
    }

    function claimFunds(uint256 amount_, address destination_) external override whenProtocolNotPaused {
        require(msg.sender == _lender, "ML:CF:NOT_LENDER");

        emit FundsClaimed(amount_, destination_);

        _claimFunds(amount_, destination_);
    }

    function fundLoan(address lender_, uint256 amount_) external override whenProtocolNotPaused returns (uint256 fundsLent_) {
        address fundsAssetAddress = _fundsAsset;

        // The amount specified is an optional amount to be transferred from the caller, as a convenience for EOAs.
        require(amount_ == uint256(0) || ERC20Helper.transferFrom(fundsAssetAddress, msg.sender, address(this), amount_), "ML:FL:TRANSFER_FROM_FAILED");

        // If the loan is not active, fund it.
        if (_nextPaymentDueDate == uint256(0)) {
            // NOTE: `_nextPaymentDueDate` emitted in event is updated by `_fundLoan`.
            emit Funded(lender_, fundsLent_ = _fundLoan(lender_), _nextPaymentDueDate);
        }

        uint256 extra         = _getUnaccountedAmount(fundsAssetAddress);
        address lenderAddress = _lender;

        // NOTE: This block is not only a stopgap solution to allow a LiquidityLockerV1 to send funds to a DebtLocker, while maintaining PoolV1 accounting,
        //       but also ensures unaccounted funds (pre-existing or due to over-funding) gets redirected to the lender.
        if (extra > uint256(0)) {
            emit FundsRedirected(extra, lenderAddress);
            require(ERC20Helper.transfer(fundsAssetAddress, lenderAddress, extra), "ML:FL:TRANSFER_FAILED");
        }
    }

    function repossess(address destination_) external override whenProtocolNotPaused returns (uint256 collateralRepossessed_, uint256 fundsRepossessed_) {
        require(msg.sender == _lender, "ML:R:NOT_LENDER");

        ( collateralRepossessed_, fundsRepossessed_ ) = _repossess(destination_);

        emit Repossessed(collateralRepossessed_, fundsRepossessed_, destination_);
    }

    function setPendingLender(address pendingLender_) external override {
        require(msg.sender == _lender, "ML:SPL:NOT_LENDER");

        emit PendingLenderSet(_pendingLender = pendingLender_);
    }

    /*******************************/
    /*** Miscellaneous Functions ***/
    /*******************************/

    function rejectNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external override whenProtocolNotPaused {
        require((msg.sender == _borrower) || (msg.sender == _lender), "L:RNT:NO_AUTH");

        emit NewTermsRejected(_rejectNewTerms(refinancer_, deadline_, calls_), refinancer_, deadline_, calls_);
    }

    function skim(address token_, address destination_) external override whenProtocolNotPaused returns (uint256 skimmed_) {
        require((msg.sender == _borrower) || (msg.sender == _lender),    "L:S:NO_AUTH");
        require((token_ != _fundsAsset) && (token_ != _collateralAsset), "L:S:INVALID_TOKEN");

        emit Skimmed(token_, skimmed_ = IERC20(token_).balanceOf(address(this)), destination_);

        require(ERC20Helper.transfer(token_, destination_, skimmed_), "L:S:TRANSFER_FAILED");
    }

    /**********************/
    /*** View Functions ***/
    /**********************/

    function getAdditionalCollateralRequiredFor(uint256 drawdown_) public view override returns (uint256 collateral_) {
        // Determine the collateral needed in the contract for a reduced drawable funds amount.
        uint256 collateralNeeded  = _getCollateralRequiredFor(_principal, _drawableFunds - drawdown_, _principalRequested, _collateralRequired);
        uint256 currentCollateral = _collateral;

        return collateralNeeded > currentCollateral ? collateralNeeded - currentCollateral : uint256(0);
    }

    function getEarlyPaymentBreakdown() external view override returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) {
        ( principal_, interest_, delegateFee_, treasuryFee_ ) = _getEarlyPaymentBreakdown();
    }

    function getNextPaymentBreakdown() external view override returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) {
        ( principal_, interest_, delegateFee_, treasuryFee_ ) = _getNextPaymentBreakdown();
    }

    function getRefinanceInterest(uint256 timestamp_) external view override returns (uint256 proRataInterest_) {
        ( proRataInterest_, ) = _getRefinanceInterestParams(
            timestamp_,
            _paymentInterval,
            _principal,
            _endingPrincipal,
            _interestRate,
            _paymentsRemaining,
            _nextPaymentDueDate,
            _lateFeeRate,
            _lateInterestPremium
        );
    }

    function isProtocolPaused() public view override returns (bool paused_) {
        return IMapleGlobalsLike(IMapleProxyFactory(_factory()).mapleGlobals()).protocolPaused();
    }

    /****************************/
    /*** State View Functions ***/
    /****************************/

    function borrower() external view override returns (address borrower_) {
        return _borrower;
    }

    function claimableFunds() external view override returns (uint256 claimableFunds_) {
        return _claimableFunds;
    }

    function collateral() external view override returns (uint256 collateral_) {
        return _collateral;
    }

    function collateralAsset() external view override returns (address collateralAsset_) {
        return _collateralAsset;
    }

    function collateralRequired() external view override returns (uint256 collateralRequired_) {
        return _collateralRequired;
    }

    function delegateFee() external view override returns (uint256 delegateFee_) {
        return _delegateFee;
    }

    function drawableFunds() external view override returns (uint256 drawableFunds_) {
        return _drawableFunds;
    }

    function earlyFeeRate() external view override returns (uint256 earlyFeeRate_) {
        return _earlyFeeRate;
    }

    function endingPrincipal() external view override returns (uint256 endingPrincipal_) {
        return _endingPrincipal;
    }

    function excessCollateral() external view override returns (uint256 excessCollateral_) {
        uint256 collateralNeeded  = _getCollateralRequiredFor(_principal, _drawableFunds, _principalRequested, _collateralRequired);
        uint256 currentCollateral = _collateral;

        return currentCollateral > collateralNeeded ? currentCollateral - collateralNeeded : uint256(0);
    }

    function factory() external view override returns (address factory_) {
        return _factory();
    }

    function fundsAsset() external view override returns (address fundsAsset_) {
        return _fundsAsset;
    }

    function gracePeriod() external view override returns (uint256 gracePeriod_) {
        return _gracePeriod;
    }

    function implementation() external view override returns (address implementation_) {
        return _implementation();
    }

    function interestRate() external view override returns (uint256 interestRate_) {
        return _interestRate;
    }

    function lateFeeRate() external view override returns (uint256 lateFeeRate_) {
        return _lateFeeRate;
    }

    function lateInterestPremium() external view override returns (uint256 lateInterestPremium_) {
        return _lateInterestPremium;
    }

    function lender() external view override returns (address lender_) {
        return _lender;
    }

    function nextPaymentDueDate() external view override returns (uint256 nextPaymentDueDate_) {
        return _nextPaymentDueDate;
    }

    function paymentInterval() external view override returns (uint256 paymentInterval_) {
        return _paymentInterval;
    }

    function paymentsRemaining() external view override returns (uint256 paymentsRemaining_) {
        return _paymentsRemaining;
    }

    function pendingBorrower() external view override returns (address pendingBorrower_) {
        return _pendingBorrower;
    }

    function pendingLender() external view override returns (address pendingLender_) {
        return _pendingLender;
    }

    function principalRequested() external view override returns (uint256 principalRequested_) {
        return _principalRequested;
    }

    function principal() external view override returns (uint256 principal_) {
        return _principal;
    }

    function refinanceCommitment() external view override returns (bytes32 refinanceCommitment_) {
        return _refinanceCommitment;
    }

    function refinanceInterest() external view override returns (uint256 refinanceInterest_) {
        return _refinanceInterest;
    }

    // NOTE: This is needed for `fundLoan` call from PoolV1.
    function superFactory() external view override returns (address superFactory_) {
        return _factory();
    }

    function treasuryFee() external view override returns (uint256 treasuryFee_) {
        return _treasuryFee;
    }

}

File 2 of 16 : MapleLoanInternals.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { IERC20 }                from "../modules/erc20/contracts/interfaces/IERC20.sol";
import { ERC20Helper }           from "../modules/erc20-helper/src/ERC20Helper.sol";
import { MapleProxiedInternals } from "../modules/maple-proxy-factory/contracts/MapleProxiedInternals.sol";

import { ILenderLike, IMapleGlobalsLike } from "./interfaces/Interfaces.sol";
import { IMapleLoanFactory }              from "./interfaces/IMapleLoanFactory.sol";

/// @title MapleLoanInternals defines the storage layout and internal logic of MapleLoan.
abstract contract MapleLoanInternals is MapleProxiedInternals {

    uint256 private constant SCALED_ONE = uint256(10 ** 18);

    // Roles
    address internal _borrower;         // The address of the borrower.
    address internal _lender;           // The address of the lender.
    address internal _pendingBorrower;  // The address of the pendingBorrower, the only address that can accept the borrower role.
    address internal _pendingLender;    // The address of the pendingLender, the only address that can accept the lender role.

    // Assets
    address internal _collateralAsset;  // The address of the asset used as collateral.
    address internal _fundsAsset;       // The address of the asset used as funds.

    // Loan Term Parameters
    uint256 internal _gracePeriod;      // The number of seconds a payment can be late.
    uint256 internal _paymentInterval;  // The number of seconds between payments.

    // Rates
    uint256 internal _interestRate;         // The annualized interest rate of the loan.
    uint256 internal _earlyFeeRate;         // The fee rate for prematurely closing loans.
    uint256 internal _lateFeeRate;          // The fee rate for late payments.
    uint256 internal _lateInterestPremium;  // The amount to increase the interest rate by for late payments.

    // Requested Amounts
    uint256 internal _collateralRequired;  // The collateral the borrower is expected to put up to draw down all _principalRequested.
    uint256 internal _principalRequested;  // The funds the borrowers wants to borrow.
    uint256 internal _endingPrincipal;     // The principal to remain at end of loan.

    // State
    uint256 internal _drawableFunds;       // The amount of funds that can be drawn down.
    uint256 internal _claimableFunds;      // The amount of funds that the lender can claim (principal repayments, interest, etc).
    uint256 internal _collateral;          // The amount of collateral, in collateral asset, that is currently posted.
    uint256 internal _nextPaymentDueDate;  // The timestamp of due date of next payment.
    uint256 internal _paymentsRemaining;   // The number of payments remaining.
    uint256 internal _principal;           // The amount of principal yet to be paid down.

    // Refinance
    bytes32 internal _refinanceCommitment;

    uint256 internal _refinanceInterest;

    // Establishment fees
    uint256 internal _delegateFee;
    uint256 internal _treasuryFee;

    /**********************************/
    /*** Internal General Functions ***/
    /**********************************/

    /// @dev Clears all state variables to end a loan, but keep borrower and lender withdrawal functionality intact.
    function _clearLoanAccounting() internal {
        _gracePeriod     = uint256(0);
        _paymentInterval = uint256(0);

        _interestRate        = uint256(0);
        _earlyFeeRate        = uint256(0);
        _lateFeeRate         = uint256(0);
        _lateInterestPremium = uint256(0);

        _endingPrincipal = uint256(0);

        _nextPaymentDueDate = uint256(0);
        _paymentsRemaining  = uint256(0);
        _principal          = uint256(0);

        _delegateFee = uint256(0);
        _treasuryFee = uint256(0);
    }

    /**
     *  @dev   Initializes the loan.
     *  @param borrower_    The address of the borrower.
     *  @param assets_      Array of asset addresses.
     *                          [0]: collateralAsset,
     *                          [1]: fundsAsset.
     *  @param termDetails_ Array of loan parameters:
     *                          [0]: gracePeriod,
     *                          [1]: paymentInterval,
     *                          [2]: payments,
     *  @param amounts_     Requested amounts:
     *                          [0]: collateralRequired,
     *                          [1]: principalRequested,
     *                          [2]: endingPrincipal.
     *  @param rates_       Fee parameters:
     *                          [0]: interestRate,
     *                          [1]: earlyFeeRate,
     *                          [2]: lateFeeRate,
     *                          [3]: lateInterestPremium.
     */
    function _initialize(
        address borrower_,
        address[2] memory assets_,
        uint256[3] memory termDetails_,
        uint256[3] memory amounts_,
        uint256[4] memory rates_
    )
        internal
    {
        // Principal requested needs to be non-zero (see `_getCollateralRequiredFor` math).
        require(amounts_[1] > uint256(0), "MLI:I:INVALID_PRINCIPAL");

        // Ending principal needs to be less than or equal to principal requested.
        require(amounts_[2] <= amounts_[1], "MLI:I:INVALID_ENDING_PRINCIPAL");

        require((_borrower = borrower_) != address(0), "MLI:I:INVALID_BORROWER");

        _collateralAsset = assets_[0];
        _fundsAsset      = assets_[1];

        _gracePeriod       = termDetails_[0];
        _paymentInterval   = termDetails_[1];
        _paymentsRemaining = termDetails_[2];

        _collateralRequired = amounts_[0];
        _principalRequested = amounts_[1];
        _endingPrincipal    = amounts_[2];

        _interestRate        = rates_[0];
        _earlyFeeRate        = rates_[1];
        _lateFeeRate         = rates_[2];
        _lateInterestPremium = rates_[3];

        _setEstablishmentFees(amounts_[1], termDetails_[1], uint256(0), uint256(0));
    }

    /**************************************/
    /*** Internal Borrow-side Functions ***/
    /**************************************/

    /// @dev Prematurely ends a loan by making all remaining payments.
    function _closeLoan() internal returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) {
        require(block.timestamp <= _nextPaymentDueDate, "MLI:CL:PAYMENT_IS_LATE");

        ( principal_, interest_, delegateFee_, treasuryFee_ ) = _getEarlyPaymentBreakdown();

        _refinanceInterest  = uint256(0);

        uint256 principalAndInterest = principal_ + interest_;

        // The drawable funds are increased by the extra funds in the contract, minus the total needed for payment.
        // NOTE: This line will revert if not enough funds were added for the full payment amount.
        _drawableFunds = (_drawableFunds + _getUnaccountedAmount(_fundsAsset)) - (principalAndInterest + delegateFee_ + treasuryFee_);

        _claimableFunds += principalAndInterest;

        _processEstablishmentFees(delegateFee_, treasuryFee_);

        _clearLoanAccounting();
    }

    /// @dev Sends `amount_` of `_drawableFunds` to `destination_`.
    function _drawdownFunds(uint256 amount_, address destination_) internal {
        _drawableFunds -= amount_;

        require(ERC20Helper.transfer(_fundsAsset, destination_, amount_), "MLI:DF:TRANSFER_FAILED");
        require(_isCollateralMaintained(),                                "MLI:DF:INSUFFICIENT_COLLATERAL");
    }

    /// @dev Makes a payment to progress the loan closer to maturity.
    function _makePayment() internal returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) {
        ( principal_, interest_, delegateFee_, treasuryFee_ ) = _getNextPaymentBreakdown();

        _refinanceInterest = uint256(0);

        uint256 principalAndInterest = principal_ + interest_;

        // The drawable funds are increased by the extra funds in the contract, minus the total needed for payment.
        // NOTE: This line will revert if not enough funds were added for the full payment amount.
        _drawableFunds = (_drawableFunds + _getUnaccountedAmount(_fundsAsset)) - (principalAndInterest + delegateFee_ + treasuryFee_);

        _claimableFunds += principalAndInterest;

        _processEstablishmentFees(delegateFee_, treasuryFee_);

        uint256 paymentsRemaining = _paymentsRemaining;

        if (paymentsRemaining == uint256(1)) {
            _clearLoanAccounting();  // Assumes `_getNextPaymentBreakdown` returns a `principal_` that is `_principal`.
        } else {
            _nextPaymentDueDate += _paymentInterval;
            _principal          -= principal_;
            _paymentsRemaining   = paymentsRemaining - uint256(1);
        }
    }

    /// @dev Registers the delivery of an amount of collateral to be posted.
    function _postCollateral() internal returns (uint256 collateralPosted_) {
        _collateral += (collateralPosted_ = _getUnaccountedAmount(_collateralAsset));
    }

    /// @dev Sets refinance commitment given refinance operations.
    function _proposeNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) internal returns (bytes32 proposedRefinanceCommitment_) {
        return _refinanceCommitment =
            calls_.length > uint256(0)
                ? _getRefinanceCommitment(refinancer_, deadline_, calls_)
                : bytes32(0);
    }

    /// @dev Sends `amount_` of `_collateral` to `destination_`.
    function _removeCollateral(uint256 amount_, address destination_) internal {
        _collateral -= amount_;

        require(ERC20Helper.transfer(_collateralAsset, destination_, amount_), "MLI:RC:TRANSFER_FAILED");
        require(_isCollateralMaintained(),                                     "MLI:RC:INSUFFICIENT_COLLATERAL");
    }

    /// @dev Registers the delivery of an amount of funds to be returned as `_drawableFunds`.
    function _returnFunds() internal returns (uint256 fundsReturned_) {
        _drawableFunds += (fundsReturned_ = _getUnaccountedAmount(_fundsAsset));
    }

    /************************************/
    /*** Internal Lend-side Functions ***/
    /************************************/

    /// @dev Processes refinance operations.
    function _acceptNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) internal returns (bytes32 acceptedRefinanceCommitment_) {
        // NOTE: A zero refinancer address and/or empty calls array will never (probabilistically) match a refinance commitment in storage.
        require(_refinanceCommitment == (acceptedRefinanceCommitment_ = _getRefinanceCommitment(refinancer_, deadline_, calls_)), "MLI:ANT:COMMITMENT_MISMATCH");

        require(refinancer_.code.length != uint256(0), "MLI:ANT:INVALID_REFINANCER");

        require(block.timestamp <= deadline_, "MLI:ANT:EXPIRED_COMMITMENT");

        uint256 preRefinancePaymentInterval = _paymentInterval;

        // Get the amount of interest owed since the last payment due date, as well as the time since the last due date
        ( uint256 proRataInterest, uint256 timeSinceLastPaymentDueDate ) = _getRefinanceInterestParams(
            block.timestamp,
            preRefinancePaymentInterval,
            _principal,
            _endingPrincipal,
            _interestRate,
            _paymentsRemaining,
            _nextPaymentDueDate,
            _lateFeeRate,
            _lateInterestPremium
        );

        // In case there is still a refinance interest, just increment it instead of setting it.
        _refinanceInterest += proRataInterest;

        // Clear refinance commitment to prevent implications of re-acceptance of another call to `_acceptNewTerms`.
        _refinanceCommitment = bytes32(0);

        uint256 length = calls_.length;
        for (uint256 i; i < length;) {
            ( bool success, ) = refinancer_.delegatecall(calls_[i]);
            require(success, "MLI:ANT:FAILED");
            unchecked { ++i; }
        }

        // Track any uncaptured establishment fees and spread over the course of new loan.
        // If `timeSinceLastPaymentDueDate` is zero, these will both equal zero.
        uint256 extraDelegateFee = (_delegateFee * timeSinceLastPaymentDueDate) / (preRefinancePaymentInterval * _paymentsRemaining);
        uint256 extraTreasuryFee = (_treasuryFee * timeSinceLastPaymentDueDate) / (preRefinancePaymentInterval * _paymentsRemaining);

        // Increment the due date to be one full payment interval from now, to restart the payment schedule with new terms.
        // NOTE: `_paymentInterval` here is possibly newly set via the above delegate calls, so cache it.
        uint256 paymentInterval  = _paymentInterval;
        _nextPaymentDueDate = block.timestamp + paymentInterval;

        // Update establishment fees.
        // NOTE: `_principal` here is possibly newly increased/decreased via the above delegate calls.
        _setEstablishmentFees(_principal, paymentInterval, extraDelegateFee, extraTreasuryFee);

        // Ensure that collateral is maintained after changes made.
        require(_isCollateralMaintained(), "MLI:ANT:INSUFFICIENT_COLLATERAL");
    }

    /// @dev Sends `amount_` of `_claimableFunds` to `destination_`.
    ///      If `amount_` is higher than `_claimableFunds` the transaction will underflow and revert.
    function _claimFunds(uint256 amount_, address destination_) internal {
        _claimableFunds -= amount_;

        require(ERC20Helper.transfer(_fundsAsset, destination_, amount_), "MLI:CF:TRANSFER_FAILED");
    }

    /// @dev Fund the loan and kick off the repayment requirements.
    function _fundLoan(address lender_) internal returns (uint256 fundsLent_) {
        require(lender_ != address(0), "MLI:FL:INVALID_LENDER");

        uint256 paymentsRemaining = _paymentsRemaining;

        // Can only fund loan if there are payments remaining (as defined by the initialization) and no payment is due yet (as set by a funding).
        require((_nextPaymentDueDate == uint256(0)) && (paymentsRemaining != uint256(0)), "MLI:FL:LOAN_ACTIVE");

        uint256 paymentInterval = _paymentInterval;

        _lender = lender_;

        _nextPaymentDueDate = block.timestamp + paymentInterval;

        // Amount funded and principal are as requested.
        fundsLent_ = _principal = _principalRequested;

        address fundsAsset = _fundsAsset;

        // Cannot under-fund loan, but over-funding results in additional funds left unaccounted for.
        require(_getUnaccountedAmount(fundsAsset) >= fundsLent_, "MLI:FL:WRONG_FUND_AMOUNT");

        _drawableFunds = fundsLent_;
    }

    /// @dev Process establishment fees for a payment.
    function _processEstablishmentFees(uint256 delegateFee_, uint256 treasuryFee_) internal {
        if (!_sendFee(_lender, ILenderLike.poolDelegate.selector, delegateFee_)) {
            _claimableFunds += delegateFee_;
        }

        if (!_sendFee(_mapleGlobals(), IMapleGlobalsLike.mapleTreasury.selector, treasuryFee_)) {
            _claimableFunds += treasuryFee_;
        }
    }

    /// @dev Explicitly cancel a proposed refinance commitment
    function _rejectNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) internal returns (bytes32 rejectedRefinanceCommitment_){
        require(_refinanceCommitment == (rejectedRefinanceCommitment_ = _getRefinanceCommitment(refinancer_, deadline_, calls_)), "MLI:RNT:COMMITMENT_MISMATCH");
        _refinanceCommitment = bytes32(0);
    }

    function _sendFee(address lookup_, bytes4 selector_, uint256 amount_) internal returns (bool success_) {
        if (amount_ == uint256(0)) return true;

        ( bool success , bytes memory data ) = lookup_.call(abi.encodeWithSelector(selector_));

        if (!success || data.length != uint256(32)) return false;

        address destination = abi.decode(data, (address));

        if (destination == address(0)) return false;

        return ERC20Helper.transfer(_fundsAsset, destination, amount_);
    }

    /// @dev Set establishment fees for funds lent, capturing unpaid establishment fees from refinance.
    function _setEstablishmentFees(uint256 fundsLent_, uint256 paymentInterval_, uint256 extraDelegateFee_, uint256 extraTreasuryFee_) internal {
        IMapleGlobalsLike globals = IMapleGlobalsLike(_mapleGlobals());

        // Store the annualized delegate fee.
        _delegateFee = (fundsLent_ * globals.investorFee() * paymentInterval_) / uint256(365 days * 10_000) + extraDelegateFee_;

        // Store the annualized treasury fee.
        _treasuryFee = (fundsLent_ * globals.treasuryFee() * paymentInterval_) / uint256(365 days * 10_000) + extraTreasuryFee_;
    }

    /// @dev Reset all state variables in order to release funds and collateral of a loan in default.
    function _repossess(address destination_) internal returns (uint256 collateralRepossessed_, uint256 fundsRepossessed_) {
        uint256 nextPaymentDueDate = _nextPaymentDueDate;

        require(
            nextPaymentDueDate != uint256(0) && (block.timestamp > nextPaymentDueDate + _gracePeriod),
            "MLI:R:NOT_IN_DEFAULT"
        );

        _clearLoanAccounting();

        // Uniquely in `_repossess`, stop accounting for all funds so that they can be swept.
        _collateral     = uint256(0);
        _claimableFunds = uint256(0);
        _drawableFunds  = uint256(0);

        address collateralAsset = _collateralAsset;

        // Either there is no collateral to repossess, or the transfer of the collateral succeeds.
        require(
            (collateralRepossessed_ = _getUnaccountedAmount(collateralAsset)) == uint256(0) ||
            ERC20Helper.transfer(collateralAsset, destination_, collateralRepossessed_),
            "MLI:R:C_TRANSFER_FAILED"
        );

        address fundsAsset = _fundsAsset;

        // Either there are no funds to repossess, or the transfer of the funds succeeds.
        require(
            (fundsRepossessed_ = _getUnaccountedAmount(fundsAsset)) == uint256(0) ||
            ERC20Helper.transfer(fundsAsset, destination_, fundsRepossessed_),
            "MLI:R:F_TRANSFER_FAILED"
        );
    }

    /*******************************/
    /*** Internal View Functions ***/
    /*******************************/

    /// @dev Returns whether the amount of collateral posted is commensurate with the amount of drawn down (outstanding) principal.
    function _isCollateralMaintained() internal view returns (bool isMaintained_) {
        return _collateral >= _getCollateralRequiredFor(_principal, _drawableFunds, _principalRequested, _collateralRequired);
    }

    /// @dev Get principal and interest breakdown for paying off the entire loan early.
    function _getEarlyPaymentBreakdown() internal view returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) {
        // Compute interest and include any uncaptured interest from refinance.
        interest_ = (((principal_ = _principal) * _earlyFeeRate) / SCALED_ONE) + _refinanceInterest;

        uint256 paymentsRemaining = _paymentsRemaining;

        delegateFee_ = _delegateFee * paymentsRemaining;
        treasuryFee_ = _treasuryFee * paymentsRemaining;
    }

    /// @dev Get principal and interest breakdown for next standard payment.
    function _getNextPaymentBreakdown() internal view returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) {
        ( principal_, interest_ ) = _getPaymentBreakdown(
            block.timestamp,
            _nextPaymentDueDate,
            _paymentInterval,
            _principal,
            _endingPrincipal,
            _paymentsRemaining,
            _interestRate,
            _lateFeeRate,
            _lateInterestPremium
        );

        // Include any uncaptured interest from refinance.
        interest_ += _refinanceInterest;

        delegateFee_ = _delegateFee;
        treasuryFee_ = _treasuryFee;
    }

    /// @dev Returns the amount of an `asset_` that this contract owns, which is not currently accounted for by its state variables.
    function _getUnaccountedAmount(address asset_) internal view returns (uint256 unaccountedAmount_) {
        return IERC20(asset_).balanceOf(address(this))
            - (asset_ == _collateralAsset ? _collateral : uint256(0))                   // `_collateral` is `_collateralAsset` accounted for.
            - (asset_ == _fundsAsset ? _claimableFunds + _drawableFunds : uint256(0));  // `_claimableFunds` and `_drawableFunds` are `_fundsAsset` accounted for.
    }

    /// @dev Returns the address of the Maple Globals contract.
    function _mapleGlobals() internal view returns (address mapleGlobals_) {
        return IMapleLoanFactory(_factory()).mapleGlobals();
    }

    /*******************************/
    /*** Internal Pure Functions ***/
    /*******************************/

    /// @dev Returns the total collateral to be posted for some drawn down (outstanding) principal and overall collateral ratio requirement.
    function _getCollateralRequiredFor(
        uint256 principal_,
        uint256 drawableFunds_,
        uint256 principalRequested_,
        uint256 collateralRequired_
    )
        internal pure returns (uint256 collateral_)
    {
        // Where (collateral / outstandingPrincipal) should be greater or equal to (collateralRequired / principalRequested).
        // NOTE: principalRequested_ cannot be 0, which is reasonable, since it means this was never a loan.
        return principal_ <= drawableFunds_ ? uint256(0) : (collateralRequired_ * (principal_ - drawableFunds_)) / principalRequested_;
    }

    /// @dev Returns principal and interest portions of a payment instalment, given generic, stateless loan parameters.
    function _getInstallment(uint256 principal_, uint256 endingPrincipal_, uint256 interestRate_, uint256 paymentInterval_, uint256 totalPayments_)
        internal pure returns (uint256 principalAmount_, uint256 interestAmount_)
    {
        /*************************************************************************************************\
         *                             |                                                                 *
         * A = installment amount      |      /                         \     /           R           \  *
         * P = principal remaining     |     |  /                 \      |   | ----------------------- | *
         * R = interest rate           | A = | | P * ( 1 + R ) ^ N | - E | * |   /             \       | *
         * N = payments remaining      |     |  \                 /      |   |  | ( 1 + R ) ^ N | - 1  | *
         * E = ending principal target |      \                         /     \  \             /      /  *
         *                             |                                                                 *
         *                             |---------------------------------------------------------------- *
         *                                                                                               *
         * - Where R           is `periodicRate`                                                         *
         * - Where (1 + R) ^ N is `raisedRate`                                                           *
         * - Both of these rates are scaled by 1e18 (e.g., 12% => 0.12 * 10 ** 18)                       *
        \*************************************************************************************************/

        uint256 periodicRate = _getPeriodicInterestRate(interestRate_, paymentInterval_);
        uint256 raisedRate   = _scaledExponent(SCALED_ONE + periodicRate, totalPayments_, SCALED_ONE);

        // NOTE: If a lack of precision in `_scaledExponent` results in a `raisedRate` smaller than one, assume it to be one and simplify the equation.
        if (raisedRate <= SCALED_ONE) return ((principal_ - endingPrincipal_) / totalPayments_, uint256(0));

        uint256 total = ((((principal_ * raisedRate) / SCALED_ONE) - endingPrincipal_) * periodicRate) / (raisedRate - SCALED_ONE);

        interestAmount_  = _getInterest(principal_, interestRate_, paymentInterval_);
        principalAmount_ = total >= interestAmount_ ? total - interestAmount_ : uint256(0);
    }

    /// @dev Returns an amount by applying an annualized and scaled interest rate, to a principal, over an interval of time.
    function _getInterest(uint256 principal_, uint256 interestRate_, uint256 interval_) internal pure returns (uint256 interest_) {
        return (principal_ * _getPeriodicInterestRate(interestRate_, interval_)) / SCALED_ONE;
    }

    /// @dev Returns total principal and interest portion of a number of payments, given generic, stateless loan parameters and loan state.
    function _getPaymentBreakdown(
        uint256 currentTime_,
        uint256 nextPaymentDueDate_,
        uint256 paymentInterval_,
        uint256 principal_,
        uint256 endingPrincipal_,
        uint256 paymentsRemaining_,
        uint256 interestRate_,
        uint256 lateFeeRate_,
        uint256 lateInterestPremium_
    )
        internal pure
        returns (uint256 principalAmount_, uint256 interestAmount_)
    {
        ( principalAmount_, interestAmount_ ) = _getInstallment(
            principal_,
            endingPrincipal_,
            interestRate_,
            paymentInterval_,
            paymentsRemaining_
        );

        principalAmount_ = paymentsRemaining_ == uint256(1) ? principal_ : principalAmount_;

        interestAmount_ += _getLateInterest(
            currentTime_,
            principal_,
            interestRate_,
            nextPaymentDueDate_,
            lateFeeRate_,
            lateInterestPremium_
        );
    }

    function _getRefinanceInterestParams(
        uint256 currentTime_,
        uint256 paymentInterval_,
        uint256 principal_,
        uint256 endingPrincipal_,
        uint256 interestRate_,
        uint256 paymentsRemaining_,
        uint256 nextPaymentDueDate_,
        uint256 lateFeeRate_,
        uint256 lateInterestPremium_
    )
        internal pure returns (uint256 refinanceInterest_, uint256 timeSinceLastPaymentDueDate_)
    {
        // If the user has made an early payment, there is no refinance interest owed.
        if (currentTime_ + paymentInterval_ < nextPaymentDueDate_) return (0, 0);

        timeSinceLastPaymentDueDate_ = currentTime_ - (nextPaymentDueDate_ - paymentInterval_);

        ( , refinanceInterest_ ) = _getInstallment(
            principal_,
            endingPrincipal_,
            interestRate_,
            timeSinceLastPaymentDueDate_,
            paymentsRemaining_
        );

        refinanceInterest_ += _getLateInterest(
            currentTime_,
            principal_,
            interestRate_,
            nextPaymentDueDate_,
            lateFeeRate_,
            lateInterestPremium_
        );
    }

    function _getLateInterest(
        uint256 currentTime_,
        uint256 principal_,
        uint256 interestRate_,
        uint256 nextPaymentDueDate_,
        uint256 lateFeeRate_,
        uint256 lateInterestPremium_
    )
        internal pure returns (uint256 lateInterest_)
    {
        if (currentTime_ <= nextPaymentDueDate_) return 0;

        // Calculates the number of full days late in seconds (will always be multiples of 86,400).
        // Rounds up and is inclusive so that if a payment is 1s late or 24h0m0s late it is 1 full day late.
        // 24h0m1s late would be two full days late.
        // (((86400n - 0n - 1n) / 86400n) + 1n) * 86400n = 86400n
        // (((86401n - 0n - 1n) / 86400n) + 1n) * 86400n = 172800n
        uint256 fullDaysLate = (((currentTime_ - nextPaymentDueDate_ - 1) / 1 days) + 1) * 1 days;

        lateInterest_ += _getInterest(principal_, interestRate_ + lateInterestPremium_, fullDaysLate);
        lateInterest_ += (lateFeeRate_ * principal_) / SCALED_ONE;
    }

    /// @dev Returns the interest rate over an interval, given an annualized interest rate.
    function _getPeriodicInterestRate(uint256 interestRate_, uint256 interval_) internal pure returns (uint256 periodicInterestRate_) {
        return (interestRate_ * interval_) / uint256(365 days);
    }

    /// @dev Returns refinance commitment given refinance parameters.
    function _getRefinanceCommitment(address refinancer_, uint256 deadline_, bytes[] calldata calls_) internal pure returns (bytes32 refinanceCommitment_) {
        return keccak256(abi.encode(refinancer_, deadline_, calls_));
    }

    /**
     *  @dev Returns exponentiation of a scaled base value.
     *
     *       Walk through example:
     *       LINE  |  base_          |  exponent_  |  one_  |  result_
     *             |  3_00           |  18         |  1_00  |  0_00
     *        A    |  3_00           |  18         |  1_00  |  1_00
     *        B    |  3_00           |  9          |  1_00  |  1_00
     *        C    |  9_00           |  9          |  1_00  |  1_00
     *        D    |  9_00           |  9          |  1_00  |  9_00
     *        B    |  9_00           |  4          |  1_00  |  9_00
     *        C    |  81_00          |  4          |  1_00  |  9_00
     *        B    |  81_00          |  2          |  1_00  |  9_00
     *        C    |  6_561_00       |  2          |  1_00  |  9_00
     *        B    |  6_561_00       |  1          |  1_00  |  9_00
     *        C    |  43_046_721_00  |  1          |  1_00  |  9_00
     *        D    |  43_046_721_00  |  1          |  1_00  |  387_420_489_00
     *        B    |  43_046_721_00  |  0          |  1_00  |  387_420_489_00
     *
     * Another implementation of this algorithm can be found in Dapphub's DSMath contract:
     * https://github.com/dapphub/ds-math/blob/ce67c0fa9f8262ecd3d76b9e4c026cda6045e96c/src/math.sol#L77
     */
    function _scaledExponent(uint256 base_, uint256 exponent_, uint256 one_) internal pure returns (uint256 result_) {
        // If exponent_ is odd, set result_ to base_, else set to one_.
        result_ = exponent_ & uint256(1) != uint256(0) ? base_ : one_;          // A

        // Divide exponent_ by 2 (overwriting itself) and proceed if not zero.
        while ((exponent_ >>= uint256(1)) != uint256(0)) {                      // B
            base_ = (base_ * base_) / one_;                                     // C

            // If exponent_ is even, go back to top.
            if (exponent_ & uint256(1) == uint256(0)) continue;

            // If exponent_ is odd, multiply result_ is multiplied by base_.
            result_ = (result_ * base_) / one_;                                 // D
        }
    }

}

File 3 of 16 : IMapleLoan.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { IMapleProxied } from "../../modules/maple-proxy-factory/contracts/interfaces/IMapleProxied.sol";

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

/// @title MapleLoan implements a primitive loan with additional functionality, and is intended to be proxied.
interface IMapleLoan is IMapleProxied, IMapleLoanEvents {

    /***********************/
    /*** State Variables ***/
    /***********************/

    /**
     *  @dev The borrower of the loan, responsible for repayments.
     */
    function borrower() external view returns (address borrower_);

    /**
     *  @dev The amount of funds that have yet to be claimed by the lender.
     */
    function claimableFunds() external view returns (uint256 claimableFunds_);

    /**
     *  @dev The amount of collateral posted against outstanding (drawn down) principal.
     */
    function collateral() external view returns (uint256 collateral_);

    /**
     *  @dev The address of the asset deposited by the borrower as collateral, if needed.
     */
    function collateralAsset() external view returns (address collateralAsset_);

    /**
     *  @dev The amount of collateral required if all of the principal required is drawn down.
     */
    function collateralRequired() external view returns (uint256 collateralRequired_);

    /**
     *  @dev The delegate establishment fee.
     */
    function delegateFee() external view returns (uint256 delegateFee_);

    /**
     *  @dev The amount of funds that have yet to be drawn down by the borrower.
     */
    function drawableFunds() external view returns (uint256 drawableFunds_);

    /**
     *  @dev The rate charged at early payments.
     *       This value should be configured so that it is less expensive to close a loan with more than one payment remaining, but
     *       more expensive to close it if on the last payment.
     */
    function earlyFeeRate() external view returns (uint256 earlyFeeRate_);

    /**
     *  @dev The portion of principal to not be paid down as part of payment installments, which would need to be paid back upon final payment.
     *       If endingPrincipal = principal, loan is interest-only.
     */
    function endingPrincipal() external view returns (uint256 endingPrincipal_);

    /**
     *  @dev The asset deposited by the lender to fund the loan.
     */
    function fundsAsset() external view returns (address fundsAsset_);

    /**
     *  @dev The amount of time the borrower has, after a payment is due, to make a payment before being in default.
     */
    function gracePeriod() external view returns (uint256 gracePeriod_);

    /**
     *  @dev The annualized interest rate (APR), in units of 1e18, (i.e. 1% is 0.01e18).
     */
    function interestRate() external view returns (uint256 interestRate_);

    /**
     *  @dev The rate charged at late payments.
     */
    function lateFeeRate() external view returns (uint256 lateFeeRate_);

    /**
     *  @dev The premium over the regular interest rate applied when paying late.
     */
    function lateInterestPremium() external view returns (uint256 lateInterestPremium_);

    /**
     *  @dev The lender of the Loan.
     */
    function lender() external view returns (address lender_);

    /**
     *  @dev The timestamp due date of the next payment.
     */
    function nextPaymentDueDate() external view returns (uint256 nextPaymentDueDate_);

    /**
     *  @dev The specified time between loan payments.
     */
    function paymentInterval() external view returns (uint256 paymentInterval_);

    /**
     *  @dev The number of payment installments remaining for the loan.
     */
    function paymentsRemaining() external view returns (uint256 paymentsRemaining_);

    /**
     *  @dev The address of the pending borrower.
     */
    function pendingBorrower() external view returns (address pendingBorrower_);

    /**
     *  @dev The address of the pending lender.
     */
    function pendingLender() external view returns (address pendingLender_);

    /**
     *  @dev The amount of principal owed (initially, the requested amount), which needs to be paid back.
     */
    function principal() external view returns (uint256 principal_);

    /**
     *  @dev The initial principal amount requested by the borrower.
     */
    function principalRequested() external view returns (uint256 principalRequested_);

    /**
     *  @dev The hash of the proposed refinance agreement.
     */
    function refinanceCommitment() external view returns (bytes32 refinanceCommitment_);

    /**
     *  @dev Amount of unpaid interest that has accrued before a refinance was accepted.
     */
    function refinanceInterest() external view returns (uint256 refinanceInterest_);

    /**
     *  @dev The factory address that deployed this contract (necessary for PoolV1 integration).
     */
    function superFactory() external view returns (address superFactory_);

    /**
     *  @dev The treasury establishment fee.
     */
    function treasuryFee() external view returns (uint256 treasuryFee_);

    /********************************/
    /*** State Changing Functions ***/
    /********************************/

    /**
     *  @dev Accept the borrower role, must be called by pendingBorrower.
     */
    function acceptBorrower() external;

    /**
     *  @dev Accept the lender role, must be called by pendingLender.
     */
    function acceptLender() external;

    /**
     *  @dev   Accept the proposed terms ans trigger refinance execution
     *  @param refinancer_ The address of the refinancer contract.
     *  @param deadline_   The deadline for accepting the new terms.
     *  @param calls_      The encoded arguments to be passed to refinancer.
     *  @param amount_     An amount to pull from the caller, if any.
     */
    function acceptNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_, uint256 amount_) external;

    /**
     *  @dev   Claim funds that have been paid (principal, interest, and late fees).
     *  @param amount_      The amount to be claimed.
     *  @param destination_ The address to send the funds.
     */
    function claimFunds(uint256 amount_, address destination_) external;

    /**
     *  @dev    Repay all principal and fees and close a loan.
     *  @param  amount_      An amount to pull from the caller, if any.
     *  @return principal_   The portion of the amount paying back principal.
     *  @return interest_    The portion of the amount paying interest.
     *  @return delegateFee_ The portion of the amount paying establishment fees to the delegate.
     *  @return treasuryFee_ The portion of the amount paying establishment fees to the treasury.
     */
    function closeLoan(uint256 amount_) external returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_);

    /**
     *  @dev    Draw down funds from the loan.
     *  @param  amount_           The amount to draw down.
     *  @param  destination_      The address to send the funds.
     *  @return collateralPosted_ The amount of additional collateral posted, if any.
     */
    function drawdownFunds(uint256 amount_, address destination_) external returns (uint256 collateralPosted_);

    /**
     *  @dev    Lend funds to the loan/borrower.
     *  @param  lender_    The address to be registered as the lender.
     *  @param  amount_    An amount to pull from the caller, if any.
     *  @return fundsLent_ The amount funded.
     */
    function fundLoan(address lender_, uint256 amount_) external returns (uint256 fundsLent_);

    /**
     *  @dev    Make a payment to the loan.
     *  @param  amount_      An amount to pull from the caller, if any.
     *  @return principal_   The portion of the amount paying back principal.
     *  @return interest_    The portion of the amount paying interest fees.
     *  @return delegateFee_ The portion of the amount paying establishment fees to the delegate.
     *  @return treasuryFee_ The portion of the amount paying establishment fees to the treasury.
     */
    function makePayment(uint256 amount_) external returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_);

    /**
     *  @dev    Post collateral to the loan.
     *  @param  amount_           An amount to pull from the caller, if any.
     *  @return collateralPosted_ The amount posted.
     */
    function postCollateral(uint256 amount_) external returns (uint256 collateralPosted_);

    /**
     *  @dev   Propose new terms for refinance
     *  @param refinancer_ The address of the refinancer contract.
     *  @param deadline_   The deadline for accepting the new terms.
     *  @param calls_      The encoded arguments to be passed to refinancer.
     */
    function proposeNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external;

    /**
     *  @dev   Nullify the current proposed terms.
     *  @param refinancer_ The address of the refinancer contract.
     *  @param deadline_   The deadline for accepting the new terms.
     *  @param calls_      The encoded arguments to be passed to refinancer.
     */
    function rejectNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external;

    /**
     *  @dev   Remove collateral from the loan (opposite of posting collateral).
     *  @param amount_      The amount removed.
     *  @param destination_ The destination to send the removed collateral.
     */
    function removeCollateral(uint256 amount_, address destination_) external;

    /**
     *  @dev    Return funds to the loan (opposite of drawing down).
     *  @param  amount_        An amount to pull from the caller, if any.
     *  @return fundsReturned_ The amount returned.
     */
    function returnFunds(uint256 amount_) external returns (uint256 fundsReturned_);

    /**
     *  @dev    Repossess collateral, and any funds, for a loan in default.
     *  @param  destination_           The address where the collateral and funds asset is to be sent, if any.
     *  @return collateralRepossessed_ The amount of collateral asset repossessed.
     *  @return fundsRepossessed_      The amount of funds asset repossessed.
     */
    function repossess(address destination_) external returns (uint256 collateralRepossessed_, uint256 fundsRepossessed_);

    /**
     *  @dev   Set the pendingBorrower to a new account.
     *  @param pendingBorrower_ The address of the new pendingBorrower.
     */
    function setPendingBorrower(address pendingBorrower_) external;

    /**
     *  @dev   Set the pendingLender to a new account.
     *  @param pendingLender_ The address of the new pendingLender.
     */
    function setPendingLender(address pendingLender_) external;

    /**
     *  @dev    Remove some token (neither fundsAsset nor collateralAsset) from the loan.
     *  @param  token_       The address of the token contract.
     *  @param  destination_ The recipient of the token.
     *  @return skimmed_     The amount of token removed from the loan.
     */
    function skim(address token_, address destination_) external returns (uint256 skimmed_);

    /**********************/
    /*** View Functions ***/
    /**********************/

    /**
     *  @dev    Returns the excess collateral that can be removed.
     *  @return excessCollateral_ The excess collateral that can be removed, if any.
     */
    function excessCollateral() external view returns (uint256 excessCollateral_);

    /**
     *  @dev    Get the additional collateral to be posted to drawdown some amount.
     *  @param  drawdown_             The amount desired to be drawn down.
     *  @return additionalCollateral_ The additional collateral that must be posted, if any.
     */
    function getAdditionalCollateralRequiredFor(uint256 drawdown_) external view returns (uint256 additionalCollateral_);

    /**
     *  @dev    Get the breakdown of the total payment needed to satisfy an early repayment.
     *  @return principal_   The portion of the total amount that will go towards principal.
     *  @return interest_    The portion of the total amount that will go towards interest fees.
     *  @return delegateFee_ The portion of the total amount that will go towards establishment fees to the delegate.
     *  @return treasuryFee_ The portion of the total amount that will go towards establishment fees to the treasury.
     */
    function getEarlyPaymentBreakdown() external view returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_);

    /**
     *  @dev    Get the breakdown of the total payment needed to satisfy the next payment installment.
     *  @return principal_   The portion of the total amount that will go towards principal.
     *  @return interest_    The portion of the total amount that will go towards interest fees.
     *  @return delegateFee_ The portion of the total amount that will go towards establishment fees to the delegate.
     *  @return treasuryFee_ The portion of the total amount that will go towards establishment fees to the treasury.
     */
    function getNextPaymentBreakdown() external view returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_);

    /**
     *  @dev    Get the extra interest that will be charged according to loan terms before refinance, based on a given timestamp.
     *  @param  timestamp_       The timestamp when the new terms will be accepted.
     *  @return proRataInterest_ The interest portion to be added in the next payment.
     */
    function getRefinanceInterest(uint256 timestamp_) external view returns (uint256 proRataInterest_);

    /**
     *  @dev    Returns whether the protocol is paused.
     *  @return paused_ A boolean indicating if protocol is paused.
     */
    function isProtocolPaused() external view returns (bool paused_);

}

File 4 of 16 : IMapleLoanEvents.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

/// @title IMapleLoanEvents defines the events for a MapleLoan.
interface IMapleLoanEvents {

    /**
     *  @dev   Borrower was accepted, and set to a new account.
     *  @param borrower_ The address of the new borrower.
     */
    event BorrowerAccepted(address indexed borrower_);

    /**
     *  @dev   Collateral was posted.
     *  @param amount_ The amount of collateral posted.
     */
    event CollateralPosted(uint256 amount_);

    /**
     *  @dev   Collateral was removed.
     *  @param amount_      The amount of collateral removed.
     *  @param destination_ The recipient of the collateral removed.
     */
    event CollateralRemoved(uint256 amount_, address indexed destination_);

    /**
     *  @dev   Establishment fees were set.
     *  @param delegateFee_ The amount that will be paid as an establishment fee to the delegate.
     *  @param treasuryFee_ The amount that will be paid as an establishment fee to the treasury.
     */
    event EstablishmentFeesSet(uint256 delegateFee_, uint256 treasuryFee_);

    /**
     *  @dev   The loan was funded.
     *  @param lender_             The address of the lender.
     *  @param amount_             The amount funded.
     *  @param nextPaymentDueDate_ The due date of the next payment.
     */
    event Funded(address indexed lender_, uint256 amount_, uint256 nextPaymentDueDate_);

    /**
     *  @dev   Funds were claimed.
     *  @param amount_      The amount of funds claimed.
     *  @param destination_ The recipient of the funds claimed.
     */
    event FundsClaimed(uint256 amount_, address indexed destination_);

    /**
     *  @dev   Funds were drawn.
     *  @param amount_      The amount of funds drawn.
     *  @param destination_ The recipient of the funds drawn down.
     */
    event FundsDrawnDown(uint256 amount_, address indexed destination_);

    /**
     *  @dev   Funds were redirected on an additional `fundLoan` call.
     *  @param amount_      The amount of funds redirected.
     *  @param destination_ The recipient of the redirected funds.
     */
    event FundsRedirected(uint256 amount_, address indexed destination_);

    /**
     *  @dev   Funds were returned.
     *  @param amount_ The amount of funds returned.
     */
    event FundsReturned(uint256 amount_);

    /**
     *  @dev   The loan was initialized.
     *  @param borrower_    The address of the borrower.
     *  @param assets_      Array of asset addresses.
     *                          [0]: collateralAsset,
     *                          [1]: fundsAsset.
     *  @param termDetails_ Array of loan parameters:
     *                          [0]: gracePeriod,
     *                          [1]: paymentInterval,
     *                          [2]: payments,
     *  @param amounts_     Requested amounts:
     *                          [0]: collateralRequired,
     *                          [1]: principalRequested,
     *                          [2]: endingPrincipal.
     *  @param rates_       Fee parameters:
     *                          [0]: interestRate,
     *                          [1]: earlyFeeRate,
     *                          [2]: lateFeeRate,
     *                          [3]: lateInterestPremium.
     */
    event Initialized(address indexed borrower_, address[2] assets_, uint256[3] termDetails_, uint256[3] amounts_, uint256[4] rates_);

    /**
     *  @dev   Lender was accepted, and set to a new account.
     *  @param lender_ The address of the new lender.
     */
    event LenderAccepted(address indexed lender_);

    /**
     *  @dev   Loan was repaid early and closed.
     *  @param principalPaid_   The portion of the total amount that went towards principal.
     *  @param interestPaid_    The portion of the total amount that went towards interest.
     *  @param delegateFeePaid_ The portion of the total amount that went towards the establishment fee for the delegate.
     *  @param treasuryFeePaid_ The portion of the total amount that went towards the establishment fee for the treasury.
     */
    event LoanClosed(uint256 principalPaid_, uint256 interestPaid_, uint256 delegateFeePaid_, uint256 treasuryFeePaid_);

    /**
     *  @dev   The terms of the refinance proposal were accepted.
     *  @param refinanceCommitment_ The hash of the refinancer, deadline, and calls proposed.
     *  @param refinancer_          The address that will execute the refinance.
     *  @param deadline_            The deadline for accepting the new terms.
     *  @param calls_               The individual calls for the refinancer contract.
     */
    event NewTermsAccepted(bytes32 refinanceCommitment_, address refinancer_, uint256 deadline_, bytes[] calls_);

    /**
     *  @dev   A refinance was proposed.
     *  @param refinanceCommitment_ The hash of the refinancer, deadline, and calls proposed.
     *  @param refinancer_          The address that will execute the refinance.
     *  @param deadline_            The deadline for accepting the new terms.
     *  @param calls_               The individual calls for the refinancer contract.
     */
    event NewTermsProposed(bytes32 refinanceCommitment_, address refinancer_, uint256 deadline_, bytes[] calls_);

    /**
     *  @dev   The terms of the refinance proposal were rejected.
     *  @param refinanceCommitment_ The hash of the refinancer, deadline, and calls proposed.
     *  @param refinancer_          The address that will execute the refinance.
     *  @param deadline_            The deadline for accepting the new terms.
     *  @param calls_               The individual calls for the refinancer contract.
     */
    event NewTermsRejected(bytes32 refinanceCommitment_, address refinancer_, uint256 deadline_, bytes[] calls_);

    /**
     *  @dev   Payments were made.
     *  @param principalPaid_   The portion of the total amount that went towards principal.
     *  @param interestPaid_    The portion of the total amount that went towards interest.
     *  @param delegateFeePaid_ The portion of the total amount that went towards the establishment fee for the delegate.
     *  @param treasuryFeePaid_ The portion of the total amount that went towards the establishment fee for the treasury.
     */
    event PaymentMade(uint256 principalPaid_, uint256 interestPaid_, uint256 delegateFeePaid_, uint256 treasuryFeePaid_);

    /**
     *  @dev   Pending borrower was set.
     *  @param pendingBorrower_ Address that can accept the borrower role.
     */
    event PendingBorrowerSet(address pendingBorrower_);

    /**
     *  @dev   Pending lender was set.
     *  @param pendingLender_ Address that can accept the lender role.
     */
    event PendingLenderSet(address pendingLender_);

    /**
     *  @dev   The loan was in default and funds and collateral was repossessed by the lender.
     *  @param collateralRepossessed_ The amount of collateral asset repossessed.
     *  @param fundsRepossessed_      The amount of funds asset repossessed.
     *  @param destination_           The recipient of the collateral and funds, if any.
     */
    event Repossessed(uint256 collateralRepossessed_, uint256 fundsRepossessed_, address indexed destination_);

    /**
     *  @dev   Some token (neither fundsAsset nor collateralAsset) was removed from the loan.
     *  @param token_       The address of the token contract.
     *  @param amount_      The amount of token remove from the loan.
     *  @param destination_ The recipient of the token.
     */
    event Skimmed(address indexed token_, uint256 amount_, address indexed destination_);

}

File 5 of 16 : IMapleLoanFactory.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { IMapleProxyFactory } from "../../modules/maple-proxy-factory/contracts/interfaces/IMapleProxyFactory.sol";

/// @title MapleLoanFactory deploys Loan instances.
interface IMapleLoanFactory is IMapleProxyFactory {

    /**
     *  @dev    Whether the proxy is a MapleLoan deployed by this factory.
     *  @param  proxy_  The address of the proxy contract.
     *  @return isLoan_ Whether the proxy is a MapleLoan deployed by this factory.
     */
    function isLoan(address proxy_) external view returns (bool isLoan_);

}

File 6 of 16 : Interfaces.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

interface ILenderLike {

    function poolDelegate() external view returns (address poolDelegate_);

}

interface IMapleGlobalsLike {

    /// @dev The address of the security admin
    function globalAdmin() external view returns (address globalAdmin_);

    /// @dev The address of the Governor responsible for management of global Maple variables.
    function governor() external view returns (address governor_);

    /// @dev The fee rate directed to Pool Delegates.
    function investorFee() external view returns (uint256 investorFee_);

    /// @dev The Treasury where all fees pass through for conversion, prior to distribution.
    function mapleTreasury() external view returns (address mapleTreasury_);

    /// @dev A boolean indicating whether the protocol is paused.
    function protocolPaused() external view returns (bool paused_);

    /// @dev The fee rate directed to the Maple Treasury.
    function treasuryFee() external view returns (uint256 treasuryFee_);

}

File 7 of 16 : IERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.7;

/// @title Interface of the ERC20 standard as defined in the EIP, including EIP-2612 permit functionality.
interface IERC20 {

    /**************/
    /*** Events ***/
    /**************/

    /**
     *  @dev   Emitted when one account has set the allowance of another account over their tokens.
     *  @param owner_   Account that tokens are approved from.
     *  @param spender_ Account that tokens are approved for.
     *  @param amount_  Amount of tokens that have been approved.
     */
    event Approval(address indexed owner_, address indexed spender_, uint256 amount_);

    /**
     *  @dev   Emitted when tokens have moved from one account to another.
     *  @param owner_     Account that tokens have moved from.
     *  @param recipient_ Account that tokens have moved to.
     *  @param amount_    Amount of tokens that have been transferred.
     */
    event Transfer(address indexed owner_, address indexed recipient_, uint256 amount_);

    /**************************/
    /*** External Functions ***/
    /**************************/

    /**
     *  @dev    Function that allows one account to set the allowance of another account over their tokens.
     *          Emits an {Approval} event.
     *  @param  spender_ Account that tokens are approved for.
     *  @param  amount_  Amount of tokens that have been approved.
     *  @return success_ Boolean indicating whether the operation succeeded.
     */
    function approve(address spender_, uint256 amount_) external returns (bool success_);

    /**
     *  @dev    Function that allows one account to decrease the allowance of another account over their tokens.
     *          Emits an {Approval} event.
     *  @param  spender_          Account that tokens are approved for.
     *  @param  subtractedAmount_ Amount to decrease approval by.
     *  @return success_          Boolean indicating whether the operation succeeded.
     */
    function decreaseAllowance(address spender_, uint256 subtractedAmount_) external returns (bool success_);

    /**
     *  @dev    Function that allows one account to increase the allowance of another account over their tokens.
     *          Emits an {Approval} event.
     *  @param  spender_     Account that tokens are approved for.
     *  @param  addedAmount_ Amount to increase approval by.
     *  @return success_     Boolean indicating whether the operation succeeded.
     */
    function increaseAllowance(address spender_, uint256 addedAmount_) external returns (bool success_);

    /**
     *  @dev   Approve by signature.
     *  @param owner_    Owner address that signed the permit.
     *  @param spender_  Spender of the permit.
     *  @param amount_   Permit approval spend limit.
     *  @param deadline_ Deadline after which the permit is invalid.
     *  @param v_        ECDSA signature v component.
     *  @param r_        ECDSA signature r component.
     *  @param s_        ECDSA signature s component.
     */
    function permit(address owner_, address spender_, uint amount_, uint deadline_, uint8 v_, bytes32 r_, bytes32 s_) external;

    /**
     *  @dev    Moves an amount of tokens from `msg.sender` to a specified account.
     *          Emits a {Transfer} event.
     *  @param  recipient_ Account that receives tokens.
     *  @param  amount_    Amount of tokens that are transferred.
     *  @return success_   Boolean indicating whether the operation succeeded.
     */
    function transfer(address recipient_, uint256 amount_) external returns (bool success_);

    /**
     *  @dev    Moves a pre-approved amount of tokens from a sender to a specified account.
     *          Emits a {Transfer} event.
     *          Emits an {Approval} event.
     *  @param  owner_     Account that tokens are moving from.
     *  @param  recipient_ Account that receives tokens.
     *  @param  amount_    Amount of tokens that are transferred.
     *  @return success_   Boolean indicating whether the operation succeeded.
     */
    function transferFrom(address owner_, address recipient_, uint256 amount_) external returns (bool success_);

    /**********************/
    /*** View Functions ***/
    /**********************/

    /**
     *  @dev    Returns the allowance that one account has given another over their tokens.
     *  @param  owner_     Account that tokens are approved from.
     *  @param  spender_   Account that tokens are approved for.
     *  @return allowance_ Allowance that one account has given another over their tokens.
     */
    function allowance(address owner_, address spender_) external view returns (uint256 allowance_);

    /**
     *  @dev    Returns the amount of tokens owned by a given account.
     *  @param  account_ Account that owns the tokens.
     *  @return balance_ Amount of tokens owned by a given account.
     */
    function balanceOf(address account_) external view returns (uint256 balance_);

    /**
     *  @dev    Returns the decimal precision used by the token.
     *  @return decimals_ The decimal precision used by the token.
     */
    function decimals() external view returns (uint8 decimals_);

    /**
     *  @dev    Returns the signature domain separator.
     *  @return domainSeparator_ The signature domain separator.
     */
    function DOMAIN_SEPARATOR() external view returns (bytes32 domainSeparator_);

    /**
     *  @dev    Returns the name of the token.
     *  @return name_ The name of the token.
     */
    function name() external view returns (string memory name_);

    /**
      *  @dev    Returns the nonce for the given owner.
      *  @param  owner_  The address of the owner account.
      *  @return nonce_ The nonce for the given owner.
     */
    function nonces(address owner_) external view returns (uint256 nonce_);

    /**
     *  @dev    Returns the permit type hash.
     *  @return permitTypehash_ The permit type hash.
     */
    function PERMIT_TYPEHASH() external view returns (bytes32 permitTypehash_);

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

    /**
     *  @dev    Returns the total amount of tokens in existence.
     *  @return totalSupply_ The total amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256 totalSupply_);

}

File 8 of 16 : ERC20Helper.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

import { IERC20Like } from "./interfaces/IERC20Like.sol";

/**
 * @title Small Library to standardize erc20 token interactions.
 */
library ERC20Helper {

    /**************************/
    /*** Internal Functions ***/
    /**************************/

    function transfer(address token_, address to_, uint256 amount_) internal returns (bool success_) {
        return _call(token_, abi.encodeWithSelector(IERC20Like.transfer.selector, to_, amount_));
    }

    function transferFrom(address token_, address from_, address to_, uint256 amount_) internal returns (bool success_) {
        return _call(token_, abi.encodeWithSelector(IERC20Like.transferFrom.selector, from_, to_, amount_));
    }

    function approve(address token_, address spender_, uint256 amount_) internal returns (bool success_) {
        // If setting approval to zero fails, return false.
        if (!_call(token_, abi.encodeWithSelector(IERC20Like.approve.selector, spender_, uint256(0)))) return false;

        // If `amount_` is zero, return true as the previous step already did this.
        if (amount_ == uint256(0)) return true;

        // Return the result of setting the approval to `amount_`.
        return _call(token_, abi.encodeWithSelector(IERC20Like.approve.selector, spender_, amount_));
    }

    function _call(address token_, bytes memory data_) private returns (bool success_) {
        if (token_.code.length == uint256(0)) return false;

        bytes memory returnData;
        ( success_, returnData ) = token_.call(data_);

        return success_ && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
    }

}

File 9 of 16 : IERC20Like.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

/// @title Interface of the ERC20 standard as needed by ERC20Helper.
interface IERC20Like {

    function approve(address spender_, uint256 amount_) external returns (bool success_);

    function transfer(address recipient_, uint256 amount_) external returns (bool success_);

    function transferFrom(address owner_, address recipient_, uint256 amount_) external returns (bool success_);

}

File 10 of 16 : MapleProxiedInternals.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { ProxiedInternals } from "../modules/proxy-factory/contracts/ProxiedInternals.sol";

/// @title A Maple implementation that is to be proxied, will need MapleProxiedInternals.
abstract contract MapleProxiedInternals is ProxiedInternals {}

File 11 of 16 : IMapleProxied.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { IProxied } from "../../modules/proxy-factory/contracts/interfaces/IProxied.sol";

/// @title A Maple implementation that is to be proxied, must implement IMapleProxied.
interface IMapleProxied is IProxied {

    /**
     *  @dev   The instance was upgraded.
     *  @param toVersion_ The new version of the loan.
     *  @param arguments_ The upgrade arguments, if any.
     */
    event Upgraded(uint256 toVersion_, bytes arguments_);

    /**
     *  @dev   Upgrades a contract implementation to a specific version.
     *         Access control logic critical since caller can force a selfdestruct via a malicious `migrator_` which is delegatecalled.
     *  @param toVersion_ The version to upgrade to.
     *  @param arguments_ Some encoded arguments to use for the upgrade.
     */
    function upgrade(uint256 toVersion_, bytes calldata arguments_) external;

}

File 12 of 16 : IMapleProxyFactory.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { IDefaultImplementationBeacon } from "../../modules/proxy-factory/contracts/interfaces/IDefaultImplementationBeacon.sol";

/// @title A Maple factory for Proxy contracts that proxy MapleProxied implementations.
interface IMapleProxyFactory is IDefaultImplementationBeacon {

    /**************/
    /*** Events ***/
    /**************/

    /**
     *  @dev   A default version was set.
     *  @param version_ The default version.
     */
    event DefaultVersionSet(uint256 indexed version_);

    /**
     *  @dev   A version of an implementation, at some address, was registered, with an optional initializer.
     *  @param version_               The version registered.
     *  @param implementationAddress_ The address of the implementation.
     *  @param initializer_           The address of the initializer, if any.
     */
    event ImplementationRegistered(uint256 indexed version_, address indexed implementationAddress_, address indexed initializer_);

    /**
     *  @dev   A proxy contract was deployed with some initialization arguments.
     *  @param version_                 The version of the implementation being proxied by the deployed proxy contract.
     *  @param instance_                The address of the proxy contract deployed.
     *  @param initializationArguments_ The arguments used to initialize the proxy contract, if any.
     */
    event InstanceDeployed(uint256 indexed version_, address indexed instance_, bytes initializationArguments_);

    /**
     *  @dev   A instance has upgraded by proxying to a new implementation, with some migration arguments.
     *  @param instance_           The address of the proxy contract.
     *  @param fromVersion_        The initial implementation version being proxied.
     *  @param toVersion_          The new implementation version being proxied.
     *  @param migrationArguments_ The arguments used to migrate, if any.
     */
    event InstanceUpgraded(address indexed instance_, uint256 indexed fromVersion_, uint256 indexed toVersion_, bytes migrationArguments_);

    /**
     *  @dev   The MapleGlobals was set.
     *  @param mapleGlobals_ The address of a Maple Globals contract.
     */
    event MapleGlobalsSet(address indexed mapleGlobals_);

    /**
     *  @dev   An upgrade path was disabled, with an optional migrator contract.
     *  @param fromVersion_ The starting version of the upgrade path.
     *  @param toVersion_   The destination version of the upgrade path.
     */
    event UpgradePathDisabled(uint256 indexed fromVersion_, uint256 indexed toVersion_);

    /**
     *  @dev   An upgrade path was enabled, with an optional migrator contract.
     *  @param fromVersion_ The starting version of the upgrade path.
     *  @param toVersion_   The destination version of the upgrade path.
     *  @param migrator_    The address of the migrator, if any.
     */
    event UpgradePathEnabled(uint256 indexed fromVersion_, uint256 indexed toVersion_, address indexed migrator_);

    /***********************/
    /*** State Variables ***/
    /***********************/

    /**
     *  @dev The default version.
     */
    function defaultVersion() external view returns (uint256 defaultVersion_);

    /**
     *  @dev The address of the MapleGlobals contract.
     */
    function mapleGlobals() external view returns (address mapleGlobals_);

    /**
     *  @dev    Whether the upgrade is enabled for a path from a version to another version.
     *  @param  toVersion_   The initial version.
     *  @param  fromVersion_ The destination version.
     *  @return allowed_     Whether the upgrade is enabled.
     */
    function upgradeEnabledForPath(uint256 toVersion_, uint256 fromVersion_) external view returns (bool allowed_);

    /********************************/
    /*** State Changing Functions ***/
    /********************************/

    /**
     *  @dev    Deploys a new instance proxying the default implementation version, with some initialization arguments.
     *          Uses a nonce and `msg.sender` as a salt for the CREATE2 opcode during instantiation to produce deterministic addresses.
     *  @param  arguments_ The initialization arguments to use for the instance deployment, if any.
     *  @param  salt_      The salt to use in the contract creation process.
     *  @return instance_  The address of the deployed proxy contract.
     */
    function createInstance(bytes calldata arguments_, bytes32 salt_) external returns (address instance_);

    /**
     *  @dev   Enables upgrading from a version to a version of an implementation, with an optional migrator.
     *         Only the Governor can call this function.
     *  @param fromVersion_ The starting version of the upgrade path.
     *  @param toVersion_   The destination version of the upgrade path.
     *  @param migrator_    The address of the migrator, if any.
     */
    function enableUpgradePath(uint256 fromVersion_, uint256 toVersion_, address migrator_) external;

    /**
     *  @dev   Disables upgrading from a version to a version of a implementation.
     *         Only the Governor can call this function.
     *  @param fromVersion_ The starting version of the upgrade path.
     *  @param toVersion_   The destination version of the upgrade path.
     */
    function disableUpgradePath(uint256 fromVersion_, uint256 toVersion_) external;

    /**
     *  @dev   Registers the address of an implementation contract as a version, with an optional initializer.
     *         Only the Governor can call this function.
     *  @param version_               The version to register.
     *  @param implementationAddress_ The address of the implementation.
     *  @param initializer_           The address of the initializer, if any.
     */
    function registerImplementation(uint256 version_, address implementationAddress_, address initializer_) external;

    /**
     *  @dev   Sets the default version.
     *         Only the Governor can call this function.
     *  @param version_ The implementation version to set as the default.
     */
    function setDefaultVersion(uint256 version_) external;

    /**
     *  @dev   Sets the Maple Globals contract.
     *         Only the Governor can call this function.
     *  @param mapleGlobals_ The address of a Maple Globals contract.
     */
    function setGlobals(address mapleGlobals_) external;

    /**
     *  @dev   Upgrades the calling proxy contract's implementation, with some migration arguments.
     *  @param toVersion_ The implementation version to upgrade the proxy contract to.
     *  @param arguments_ The migration arguments, if any.
     */
    function upgradeInstance(uint256 toVersion_, bytes calldata arguments_) external;

    /**********************/
    /*** View Functions ***/
    /**********************/

    /**
     *  @dev    Returns the deterministic address of a potential proxy, given some arguments and salt.
     *  @param  arguments_       The initialization arguments to be used when deploying the proxy.
     *  @param  salt_            The salt to be used when deploying the proxy.
     *  @return instanceAddress_ The deterministic address of a potential proxy.
     */
    function getInstanceAddress(bytes calldata arguments_, bytes32 salt_) external view returns (address instanceAddress_);

    /**
     *  @dev    Returns the address of an implementation version.
     *  @param  version_        The implementation version.
     *  @return implementation_ The address of the implementation.
     */
    function implementationOf(uint256 version_) external view returns (address implementation_);

    /**
     *  @dev    Returns the address of a migrator contract for a migration path (from version, to version).
     *          If oldVersion_ == newVersion_, the migrator is an initializer.
     *  @param  oldVersion_ The old version.
     *  @param  newVersion_ The new version.
     *  @return migrator_   The address of a migrator contract.
     */
    function migratorForPath(uint256 oldVersion_, uint256 newVersion_) external view returns (address migrator_);

    /**
     *  @dev    Returns the version of an implementation contract.
     *  @param  implementation_ The address of an implementation contract.
     *  @return version_        The version of the implementation contract.
     */
    function versionOf(address implementation_) external view returns (uint256 version_);

}

File 13 of 16 : ProxiedInternals.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

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

/// @title An implementation that is to be proxied, will need ProxiedInternals.
abstract contract ProxiedInternals is SlotManipulatable {

    /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.factory') - 1`.
    bytes32 private constant FACTORY_SLOT = bytes32(0x7a45a402e4cb6e08ebc196f20f66d5d30e67285a2a8aa80503fa409e727a4af1);

    /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`.
    bytes32 private constant IMPLEMENTATION_SLOT = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);

    /// @dev Delegatecalls to a migrator contract to manipulate storage during an initialization or migration.
    function _migrate(address migrator_, bytes calldata arguments_) internal virtual returns (bool success_) {
        uint256 size;

        assembly {
            size := extcodesize(migrator_)
        }

        if (size == uint256(0)) return false;

        ( success_, ) = migrator_.delegatecall(arguments_);
    }

    /// @dev Sets the factory address in storage.
    function _setFactory(address factory_) internal virtual returns (bool success_) {
        _setSlotValue(FACTORY_SLOT, bytes32(uint256(uint160(factory_))));
        return true;
    }

    /// @dev Sets the implementation address in storage.
    function _setImplementation(address implementation_) internal virtual returns (bool success_) {
        _setSlotValue(IMPLEMENTATION_SLOT, bytes32(uint256(uint160(implementation_))));
        return true;
    }

    /// @dev Returns the factory address.
    function _factory() internal view virtual returns (address factory_) {
        return address(uint160(uint256(_getSlotValue(FACTORY_SLOT))));
    }

    /// @dev Returns the implementation address.
    function _implementation() internal view virtual returns (address implementation_) {
        return address(uint160(uint256(_getSlotValue(IMPLEMENTATION_SLOT))));
    }

}

File 14 of 16 : SlotManipulatable.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

abstract contract SlotManipulatable {

    function _getReferenceTypeSlot(bytes32 slot_, bytes32 key_) internal pure returns (bytes32 value_) {
        return keccak256(abi.encodePacked(key_, slot_));
    }

    function _getSlotValue(bytes32 slot_) internal view returns (bytes32 value_) {
        assembly {
            value_ := sload(slot_)
        }
    }

    function _setSlotValue(bytes32 slot_, bytes32 value_) internal {
        assembly {
            sstore(slot_, value_)
        }
    }

}

File 15 of 16 : IDefaultImplementationBeacon.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

/// @title An beacon that provides a default implementation for proxies, must implement IDefaultImplementationBeacon.
interface IDefaultImplementationBeacon {

    /// @dev The address of an implementation for proxies.
    function defaultImplementation() external view returns (address defaultImplementation_);

}

File 16 of 16 : IProxied.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

/// @title An implementation that is to be proxied, must implement IProxied.
interface IProxied {

    /**
     *  @dev The address of the proxy factory.
     */
    function factory() external view returns (address factory_);

    /**
     *  @dev The address of the implementation contract being proxied.
     */
    function implementation() external view returns (address implementation_);

    /**
     *  @dev   Modifies the proxy's implementation address.
     *  @param newImplementation_ The address of an implementation contract.
     */
    function setImplementation(address newImplementation_) external;

    /**
     *  @dev   Modifies the proxy's storage by delegate-calling a migrator contract with some arguments.
     *         Access control logic critical since caller can force a selfdestruct via a malicious `migrator_` which is delegatecalled.
     *  @param migrator_  The address of a migrator contract.
     *  @param arguments_ Some encoded arguments to use for the migration.
     */
    function migrate(address migrator_, bytes calldata arguments_) external;

}

Settings
{
  "remappings": [
    "contract-test-utils/=modules/contract-test-utils/contracts/",
    "erc20-helper/=modules/erc20-helper/src/",
    "erc20/=modules/erc20/",
    "maple-proxy-factory/=modules/maple-proxy-factory/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower_","type":"address"}],"name":"BorrowerAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"CollateralPosted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"CollateralRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delegateFee_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"name":"EstablishmentFeesSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nextPaymentDueDate_","type":"uint256"}],"name":"Funded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"FundsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"FundsDrawnDown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"FundsRedirected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"FundsReturned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower_","type":"address"},{"indexed":false,"internalType":"address[2]","name":"assets_","type":"address[2]"},{"indexed":false,"internalType":"uint256[3]","name":"termDetails_","type":"uint256[3]"},{"indexed":false,"internalType":"uint256[3]","name":"amounts_","type":"uint256[3]"},{"indexed":false,"internalType":"uint256[4]","name":"rates_","type":"uint256[4]"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender_","type":"address"}],"name":"LenderAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"principalPaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestPaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegateFeePaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryFeePaid_","type":"uint256"}],"name":"LoanClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"},{"indexed":false,"internalType":"address","name":"refinancer_","type":"address"},{"indexed":false,"internalType":"uint256","name":"deadline_","type":"uint256"},{"indexed":false,"internalType":"bytes[]","name":"calls_","type":"bytes[]"}],"name":"NewTermsAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"},{"indexed":false,"internalType":"address","name":"refinancer_","type":"address"},{"indexed":false,"internalType":"uint256","name":"deadline_","type":"uint256"},{"indexed":false,"internalType":"bytes[]","name":"calls_","type":"bytes[]"}],"name":"NewTermsProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"},{"indexed":false,"internalType":"address","name":"refinancer_","type":"address"},{"indexed":false,"internalType":"uint256","name":"deadline_","type":"uint256"},{"indexed":false,"internalType":"bytes[]","name":"calls_","type":"bytes[]"}],"name":"NewTermsRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"principalPaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestPaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegateFeePaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryFeePaid_","type":"uint256"}],"name":"PaymentMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingBorrower_","type":"address"}],"name":"PendingBorrowerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingLender_","type":"address"}],"name":"PendingLenderSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"collateralRepossessed_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fundsRepossessed_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"Repossessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"Skimmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"toVersion_","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"arguments_","type":"bytes"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"acceptBorrower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptLender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"refinancer_","type":"address"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"bytes[]","name":"calls_","type":"bytes[]"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"acceptNewTerms","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrower","outputs":[{"internalType":"address","name":"borrower_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"destination_","type":"address"}],"name":"claimFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimableFunds","outputs":[{"internalType":"uint256","name":"claimableFunds_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"closeLoan","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"interest_","type":"uint256"},{"internalType":"uint256","name":"delegateFee_","type":"uint256"},{"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"uint256","name":"collateral_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralAsset","outputs":[{"internalType":"address","name":"collateralAsset_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralRequired","outputs":[{"internalType":"uint256","name":"collateralRequired_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delegateFee","outputs":[{"internalType":"uint256","name":"delegateFee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"drawableFunds","outputs":[{"internalType":"uint256","name":"drawableFunds_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"destination_","type":"address"}],"name":"drawdownFunds","outputs":[{"internalType":"uint256","name":"collateralPosted_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"earlyFeeRate","outputs":[{"internalType":"uint256","name":"earlyFeeRate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endingPrincipal","outputs":[{"internalType":"uint256","name":"endingPrincipal_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"excessCollateral","outputs":[{"internalType":"uint256","name":"excessCollateral_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"factory_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lender_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"fundLoan","outputs":[{"internalType":"uint256","name":"fundsLent_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fundsAsset","outputs":[{"internalType":"address","name":"fundsAsset_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"drawdown_","type":"uint256"}],"name":"getAdditionalCollateralRequiredFor","outputs":[{"internalType":"uint256","name":"collateral_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEarlyPaymentBreakdown","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"interest_","type":"uint256"},{"internalType":"uint256","name":"delegateFee_","type":"uint256"},{"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNextPaymentBreakdown","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"interest_","type":"uint256"},{"internalType":"uint256","name":"delegateFee_","type":"uint256"},{"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp_","type":"uint256"}],"name":"getRefinanceInterest","outputs":[{"internalType":"uint256","name":"proRataInterest_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gracePeriod","outputs":[{"internalType":"uint256","name":"gracePeriod_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"implementation_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestRate","outputs":[{"internalType":"uint256","name":"interestRate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isProtocolPaused","outputs":[{"internalType":"bool","name":"paused_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lateFeeRate","outputs":[{"internalType":"uint256","name":"lateFeeRate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lateInterestPremium","outputs":[{"internalType":"uint256","name":"lateInterestPremium_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lender","outputs":[{"internalType":"address","name":"lender_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"makePayment","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"interest_","type":"uint256"},{"internalType":"uint256","name":"delegateFee_","type":"uint256"},{"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"migrator_","type":"address"},{"internalType":"bytes","name":"arguments_","type":"bytes"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextPaymentDueDate","outputs":[{"internalType":"uint256","name":"nextPaymentDueDate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentInterval","outputs":[{"internalType":"uint256","name":"paymentInterval_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentsRemaining","outputs":[{"internalType":"uint256","name":"paymentsRemaining_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingBorrower","outputs":[{"internalType":"address","name":"pendingBorrower_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingLender","outputs":[{"internalType":"address","name":"pendingLender_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"postCollateral","outputs":[{"internalType":"uint256","name":"collateralPosted_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"principal","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"principalRequested","outputs":[{"internalType":"uint256","name":"principalRequested_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"refinancer_","type":"address"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"bytes[]","name":"calls_","type":"bytes[]"}],"name":"proposeNewTerms","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refinanceCommitment","outputs":[{"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"refinanceInterest","outputs":[{"internalType":"uint256","name":"refinanceInterest_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"refinancer_","type":"address"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"bytes[]","name":"calls_","type":"bytes[]"}],"name":"rejectNewTerms","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"destination_","type":"address"}],"name":"removeCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"destination_","type":"address"}],"name":"repossess","outputs":[{"internalType":"uint256","name":"collateralRepossessed_","type":"uint256"},{"internalType":"uint256","name":"fundsRepossessed_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"returnFunds","outputs":[{"internalType":"uint256","name":"fundsReturned_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation_","type":"address"}],"name":"setImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingBorrower_","type":"address"}],"name":"setPendingBorrower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingLender_","type":"address"}],"name":"setPendingLender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"destination_","type":"address"}],"name":"skim","outputs":[{"internalType":"uint256","name":"skimmed_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"superFactory","outputs":[{"internalType":"address","name":"superFactory_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasuryFee","outputs":[{"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"toVersion_","type":"uint256"},{"internalType":"bytes","name":"arguments_","type":"bytes"}],"name":"upgrade","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b5061379b806100206000396000f3fe608060405234801561001057600080fd5b50600436106103425760003560e01c80637c3a00fd116101b8578063ba5d307811610104578063d05951a0116100a2578063d8dfeb451161007c578063d8dfeb4514610634578063dac885611461063c578063e44b387514610654578063e920b1e11461065c57600080fd5b8063d05951a0146105fb578063d41ddc961461060e578063d784d4261461062157600080fd5b8063c3fbb6fd116100de578063c3fbb6fd146105cd578063c45a015514610368578063cc32d176146105e0578063ccc04484146105e857600080fd5b8063ba5d3078146105ac578063ba83276b146105b4578063bcead63e146105bc57600080fd5b8063a97d116111610171578063b69410de1161014b578063b69410de1461058c578063b86a513e14610594578063b96b5c991461059c578063b9b1f4e3146105a457600080fd5b8063a97d116114610560578063aabaecd614610568578063acb522b41461057957600080fd5b80637c3a00fd146105115780637df1f1b91461051957806387accaf11461052a5780638ffc92151461053d5780639e10320b14610545578063a06db7dc1461055857600080fd5b806345755dd6116102925780635eeb53b411610230578063712b772f1161020a578063712b772f146104e657806375a20676146104f957806377b3c55c146105015780637a0e6fa11461050957600080fd5b80635eeb53b4146104bc57806369458ba7146104cd578063700f5006146104d557600080fd5b806350acb4ee1161026c57806350acb4ee1461045b57806350f2012f1461046e5780635114cb52146104815780635c60da1b146104b457600080fd5b806345755dd61461040d57806347350e9f146104205780634eac42351461044857600080fd5b8063267f4ac3116102ff578063390d6855116102d9578063390d6855146103ce57806339ba9f86146103e15780633b99bcee146103f25780634003f34d1461040557600080fd5b8063267f4ac3146103ab5780632ead1098146103be57806330fea1ce146103c657600080fd5b806301daa38f146103475780630895326f146103515780630d49b38c146103685780630fe3d9b7146103885780631cc1cf46146103905780631f3f19ab14610398575b600080fd5b61034f61066f565b005b6013545b6040519081526020015b60405180910390f35b610370610715565b6040516001600160a01b03909116815260200161035f565b61034f610724565b600754610355565b61034f6103a636600461326c565b6107c7565b61034f6103b936600461326c565b61086c565b600b54610355565b600954610355565b61034f6103dc366004613474565b610908565b6005546001600160a01b0316610370565b61034f610400366004613499565b6109cb565b601254610355565b61035561041b366004613442565b610b3d565b61043361042e36600461326c565b610c12565b6040805192835260208301919091520161035f565b610355610456366004613442565b610cde565b61034f6104693660046133bc565b610d26565b61035561047c366004613442565b610f46565b61049461048f366004613442565b611002565b60408051948552602085019390935291830152606082015260800161035f565b610370611147565b6003546001600160a01b0316610370565b610494611151565b6002546001600160a01b0316610370565b6103556104f43660046132a6565b61116d565b600c54610355565b600a54610355565b610355611364565b600854610355565b6000546001600160a01b0316610370565b61034f610538366004613360565b6113a0565b600d54610355565b610355610553366004613442565b6114b5565b600654610355565b601654610355565b6004546001600160a01b0316610370565b61034f610587366004613360565b6114df565b601754610355565b600e54610355565b610494611590565b600f54610355565b601454610355565b601554610355565b6001546001600160a01b0316610370565b61034f6105db3660046132df565b61159e565b601854610355565b6103556105f6366004613474565b611643565b610494610609366004613442565b611752565b61034f61061c366004613474565b61188b565b61034f61062f36600461326c565b61194c565b601154610355565b6106446119d8565b604051901515815260200161035f565b601054610355565b61035561066a366004613334565b611ac2565b6002546001600160a01b031633146106ce5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a41423a4e4f545f50454e44494e475f424f52524f57455200000000000060448201526064015b60405180910390fd5b600280546001600160a01b031990811690915560008054339216821781556040517f29bac0ac2b15405bfcc160bb74b6ae7a559b7674ce33db80785ada73e38204d29190a2565b600061071f611c6a565b905090565b6003546001600160a01b0316331461077e5760405162461bcd60e51b815260206004820152601860248201527f4d4c3a414c3a4e4f545f50454e44494e475f4c454e444552000000000000000060448201526064016106c5565b600380546001600160a01b031990811690915560018054339216821790556040517fd6165838d2e3db87aa1002b548048673fc6427eefbd1b914e100f3a0deae23e390600090a2565b6000546001600160a01b031633146108175760405162461bcd60e51b815260206004820152601360248201527226a61d29a8211d2727aa2fa127a92927aba2a960691b60448201526064016106c5565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f10f06072822ef73860fedb88933f968d20bb4aadce8a8d360d1124cb6ce1e0b2906020015b60405180910390a150565b6001546001600160a01b031633146108ba5760405162461bcd60e51b815260206004820152601160248201527026a61d29a8261d2727aa2fa622a72222a960791b60448201526064016106c5565b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527fa3ab02442c80a4102475683f16513c9139a89142be9db9804edfcfbb379fc49290602001610861565b6109106119d8565b1561092d5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b0316331461097a5760405162461bcd60e51b815260206004820152601060248201526f26a61d21a31d2727aa2fa622a72222a960811b60448201526064016106c5565b806001600160a01b03167f6bd56533ce1c8ea03f7b858ac441b5a86d140a793a7c9e3faecbbe517c2c8791836040516109b591815260200190565b60405180910390a26109c78282611c99565b5050565b6109d3611d0c565b6001600160a01b031663ec9a93686040518163ffffffff1660e01b815260040160206040518083038186803b158015610a0b57600080fd5b505afa158015610a1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a439190613289565b6001600160a01b0316336001600160a01b031614610a945760405162461bcd60e51b815260206004820152600e60248201526d26a61d2a9d2727aa2fa0a226a4a760911b60448201526064016106c5565b7faaaa7ee6b0c2f4ee1fa7312c7d5b3623a434da5a1a9ce3cb6e629caa23454ab6838383604051610ac793929190613653565b60405180910390a1610ad7611c6a565b6001600160a01b031663fe69f7088484846040518463ffffffff1660e01b8152600401610b0693929190613653565b600060405180830381600087803b158015610b2057600080fd5b505af1158015610b34573d6000803e3d6000fd5b50505050505050565b6000610b476119d8565b15610b645760405162461bcd60e51b81526004016106c590613627565b811580610b845750600554610b84906001600160a01b0316333085611d86565b610bd05760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a52463a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b7f278e51d3323fbf18b9fb8df3f8b97e31b145bc1146c52e764cf4aa1bfc4ba17d610bf9611dfd565b60405181815290925060200160405180910390a1919050565b600080610c1d6119d8565b15610c3a5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b03163314610c865760405162461bcd60e51b815260206004820152600f60248201526e26a61d291d2727aa2fa622a72222a960891b60448201526064016106c5565b610c8f83611e33565b60408051838152602081018390529294509092506001600160a01b038516917f027e623aab0b174da270ff529cad1c54f09182651e07437d2ac557929b9e5b49910160405180910390a2915091565b600080610d0060145484600f54610cf5919061370d565b600d54600c54611fa8565b601154909150808211610d14576000610d1e565b610d1e818361370d565b949350505050565b610d2e6119d8565b15610d4b5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b0316338114610d9a5760405162461bcd60e51b815260206004820152601160248201527026a61d20a72a1d2727aa2fa622a72222a960791b60448201526064016106c5565b6005546001600160a01b0316821580610dba5750610dba81333086611d86565b610e065760405162461bcd60e51b815260206004820152601b60248201527f4d4c3a414e543a5452414e534645525f46524f4d5f4641494c4544000000000060448201526064016106c5565b7f7150c332bd889236b6ab42cc34f0853631ceb58827f58a8697b682f13e390a8c610e3388888888611fe1565b88888888604051610e489594939291906135f9565b60405180910390a17ffe9a32948c4b8ec5c8a8eddeacd3f3621458e8bde95b725b625e5c8f4f2cb54d601754601854604051610e8e929190918252602082015260400190565b60405180910390a16000610ea1826122e0565b90508015610f3c57826001600160a01b03167ff505854d1244de20a434e0eca67ec8de6d69504f7f85594c61102ab4d9a278f382604051610ee491815260200190565b60405180910390a2610ef78284836123ba565b610f3c5760405162461bcd60e51b815260206004820152601660248201527513530e9053950e9514905394d1915497d1905253115160521b60448201526064016106c5565b5050505050505050565b6000610f506119d8565b15610f6d5760405162461bcd60e51b81526004016106c590613627565b811580610f8d5750600454610f8d906001600160a01b0316333085611d86565b610fd95760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a50433a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b7f437d44b2c697fb69e2b2f25f57fd844e376c25ed28ed5a9c4be88aa1e5c87d12610bf96123f7565b6000806000806000600f54905060008614806110315750600554611031906001600160a01b0316333089611d86565b61107d5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a4d503a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b611085612423565b600054939850919650945092506001600160a01b03163314806110aa575080600f5410155b6110f65760405162461bcd60e51b815260206004820152601960248201527f4d4c3a4d503a43414e4e4f545f5553455f4452415741424c450000000000000060448201526064016106c5565b6040805186815260208101869052908101849052606081018390527f95c4acf903eb698cf367efaaf79a8a58fb4554fcd8503b62af9f9c1b68b59e1e906080015b60405180910390a1509193509193565b600061071f61251a565b60008060008061115f612544565b929791965094509092509050565b60006111776119d8565b156111945760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b03163314806111b757506001546001600160a01b031633145b6111f15760405162461bcd60e51b815260206004820152600b60248201526a09874a6749c9ebe82aaa8960ab1b60448201526064016106c5565b6005546001600160a01b0384811691161480159061121d57506004546001600160a01b03848116911614155b61125d5760405162461bcd60e51b8152602060048201526011602482015270261d299d24a72b20a624a22faa27a5a2a760791b60448201526064016106c5565b6040516370a0823160e01b81523060048201526001600160a01b0380841691908516907ff1f6a55e7ad487ac8dd8e1d4517348d3b410a7a0bc405ef87b09078dc51b23b69082906370a082319060240160206040518083038186803b1580156112c557600080fd5b505afa1580156112d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112fd919061345b565b60405181815290945060200160405180910390a361131c8383836123ba565b61135e5760405162461bcd60e51b8152602060048201526013602482015272130e94ce9514905394d1915497d19052531151606a1b60448201526064016106c5565b92915050565b60008061137b601454600f54600d54600c54611fa8565b60115490915081811161138f576000611399565b611399828261370d565b9250505090565b6113a86119d8565b156113c55760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146114155760405162461bcd60e51b815260206004820152601360248201527226a61d28272a1d2727aa2fa127a92927aba2a960691b60448201526064016106c5565b428310156114655760405162461bcd60e51b815260206004820152601760248201527f4d4c3a504e543a494e56414c49445f444541444c494e4500000000000000000060448201526064016106c5565b7ff94d2f0322894aaf1bce14561461a8b8b6c9b11a77bbe80f20b804da8a95e4b7611492858585856125ab565b858585856040516114a79594939291906135f9565b60405180910390a150505050565b60006114d882600754601454600e54600854601354601254600a54600b546125d3565b5092915050565b6114e76119d8565b156115045760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633148061152757506001546001600160a01b031633145b6115635760405162461bcd60e51b815260206004820152600d60248201526c09874a49ca8749c9ebe82aaa89609b1b60448201526064016106c5565b7f47244a449377da5fd10e98d86d118dee442e842fc34f05179c973cfcff6acba761149285858585612642565b60008060008061115f6126b0565b6115a6611c6a565b6001600160a01b0316336001600160a01b0316146115f95760405162461bcd60e51b815260206004820152601060248201526f4d4c3a4d3a4e4f545f464143544f525960801b60448201526064016106c5565b6116048383836126fb565b61163e5760405162461bcd60e51b815260206004820152600b60248201526a13530e934e91905253115160aa1b60448201526064016106c5565b505050565b600061164d6119d8565b1561166a5760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146116b95760405162461bcd60e51b815260206004820152601260248201527126a61d22231d2727aa2fa127a92927aba2a960711b60448201526064016106c5565b816001600160a01b03167f7578fe8c4d9f6fc38fdad20d219b0ce47d38bbf8a72bdb26867809f24119363d846040516116f491815260200190565b60405180910390a2600061170784610cde565b9050801561174857600454600090611727906001600160a01b03166122e0565b905061174481831161173a576000610f46565b61047c828461370d565b9250505b6114d88484612774565b6000806000806000600f54905060008614806117815750600554611781906001600160a01b0316333089611d86565b6117cd5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a434c3a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b6117d561283b565b600054939850919650945092506001600160a01b03163314806117fa575080600f5410155b6118465760405162461bcd60e51b815260206004820152601960248201527f4d4c3a434c3a43414e4e4f545f5553455f4452415741424c450000000000000060448201526064016106c5565b6040805186815260208101869052908101849052606081018390527f6d5b31efac20a15ed5b9e27e38cf9ebcc3ffb6d64feb827a35ef84a607e8dfaf90608001611137565b6118936119d8565b156118b05760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146118ff5760405162461bcd60e51b815260206004820152601260248201527126a61d29219d2727aa2fa127a92927aba2a960711b60448201526064016106c5565b806001600160a01b03167f97b446ee2df422b7273fe6d658674835f9de3319d131c229f9a2f8ed62a761938360405161193a91815260200190565b60405180910390a26109c78282612929565b611954611c6a565b6001600160a01b0316336001600160a01b0316146119a85760405162461bcd60e51b81526020600482015260116024820152704d4c3a53493a4e4f545f464143544f525960781b60448201526064016106c5565b6001600160a01b03167f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b50565b60006119e2611c6a565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a1a57600080fd5b505afa158015611a2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a529190613289565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8a57600080fd5b505afa158015611a9e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071f9190613420565b6000611acc6119d8565b15611ae95760405162461bcd60e51b81526004016106c590613627565b6005546001600160a01b0316821580611b095750611b0981333086611d86565b611b555760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a464c3a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b601254611baf57836001600160a01b03167fcd909ec339185c4598a4096e174308fbdf136d117f230960f873a2f2e81f63af611b90866129f0565b6012546040805183815260208101929092529195500160405180910390a25b6000611bba826122e0565b6001549091506001600160a01b03168115611c6157806001600160a01b03167ff505854d1244de20a434e0eca67ec8de6d69504f7f85594c61102ab4d9a278f383604051611c0a91815260200190565b60405180910390a2611c1d8382846123ba565b611c615760405162461bcd60e51b815260206004820152601560248201527413530e91930e9514905394d1915497d19052531151605a1b60448201526064016106c5565b50505092915050565b6000611c947f7a45a402e4cb6e08ebc196f20f66d5d30e67285a2a8aa80503fa409e727a4af15490565b919050565b8160106000828254611cab919061370d565b9091555050600554611cc7906001600160a01b031682846123ba565b6109c75760405162461bcd60e51b81526020600482015260166024820152751353124e90d18e9514905394d1915497d1905253115160521b60448201526064016106c5565b6000611d16611c6a565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d4e57600080fd5b505afa158015611d62573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071f9190613289565b6040516001600160a01b0380851660248301528316604482015260648101829052600090611df49086906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612b3a565b95945050505050565b600554600090611e15906001600160a01b03166122e0565b905080600f6000828254611e2991906136b4565b9250508190555090565b60125460009081908015801590611e555750600654611e5290826136b4565b42115b611e985760405162461bcd60e51b81526020600482015260146024820152731353124e948e9393d517d25397d111519055531560621b60448201526064016106c5565b611ea0612bda565b600060118190556010819055600f8190556004546001600160a01b031690611ec7826122e0565b9450841480611edc5750611edc8186866123ba565b611f285760405162461bcd60e51b815260206004820152601760248201527f4d4c493a523a435f5452414e534645525f4641494c454400000000000000000060448201526064016106c5565b6005546001600160a01b03166000611f3f826122e0565b9450841480611f545750611f548187866123ba565b611fa05760405162461bcd60e51b815260206004820152601760248201527f4d4c493a523a465f5452414e534645525f4641494c454400000000000000000060448201526064016106c5565b505050915091565b600083851115611fd65782611fbd858761370d565b611fc790846136ee565b611fd191906136cc565b611df4565b506000949350505050565b6000611fef85858585612c18565b905080601554146120425760405162461bcd60e51b815260206004820152601b60248201527f4d4c493a414e543a434f4d4d49544d454e545f4d49534d41544348000000000060448201526064016106c5565b6001600160a01b0385163b6120995760405162461bcd60e51b815260206004820152601a60248201527f4d4c493a414e543a494e56414c49445f524546494e414e43455200000000000060448201526064016106c5565b834211156120e95760405162461bcd60e51b815260206004820152601a60248201527f4d4c493a414e543a455850495245445f434f4d4d49544d454e5400000000000060448201526064016106c5565b600060075490506000806121124284601454600e54600854601354601254600a54600b546125d3565b91509150816016600082825461212891906136b4565b90915550506000601581905585905b818110156122085760008a6001600160a01b031689898481811061215d5761215d61373a565b905060200281019061216f919061366d565b60405161217d929190613586565b600060405180830381855af49150503d80600081146121b8576040519150601f19603f3d011682016040523d82523d6000602084013e6121bd565b606091505b50509050806121ff5760405162461bcd60e51b815260206004820152600e60248201526d1353124e9053950e91905253115160921b60448201526064016106c5565b50600101612137565b5060006013548561221991906136ee565b8360175461222791906136ee565b61223191906136cc565b905060006013548661224391906136ee565b8460185461225191906136ee565b61225b91906136cc565b60075490915061226b81426136b4565b60125560145461227d90828585612c51565b612285612daf565b6122d15760405162461bcd60e51b815260206004820152601f60248201527f4d4c493a414e543a494e53554646494349454e545f434f4c4c41544552414c0060448201526064016106c5565b50505050505050949350505050565b6005546000906001600160a01b038381169116146122ff57600061230f565b600f5460105461230f91906136b4565b6004546001600160a01b0384811691161461232b57600061232f565b6011545b6040516370a0823160e01b81523060048201526001600160a01b038516906370a082319060240160206040518083038186803b15801561236e57600080fd5b505afa158015612382573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a6919061345b565b6123b0919061370d565b61135e919061370d565b6040516001600160a01b0383166024820152604481018290526000906123ed90859063a9059cbb60e01b90606401611dbd565b90505b9392505050565b60045460009061240f906001600160a01b03166122e0565b90508060116000828254611e2991906136b4565b6000806000806124316126b0565b600060168190559397509195509350915061244c84866136b4565b90508161245984836136b4565b61246391906136b4565b600554612478906001600160a01b03166122e0565b600f5461248591906136b4565b61248f919061370d565b600f8190555080601060008282546124a791906136b4565b909155506124b790508383612dcf565b60135460018114156124d0576124cb612bda565b612512565b600754601260008282546124e491906136b4565b9250508190555085601460008282546124fd919061370d565b9091555061250e905060018261370d565b6013555b505090919293565b6000611c947f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b600080600080601654670de0b6b3a764000060095460145496508661256991906136ee565b61257391906136cc565b61257d91906136b4565b601354601754919450906125929082906136ee565b9250806018546125a291906136ee565b91505090919293565b6000816125b95760006125c5565b6125c585858585612c18565b601581905595945050505050565b600080846125e18b8d6136b4565b10156125f257506000905080612634565b6125fc8a8661370d565b612606908c61370d565b9050612615898989848a612e41565b925061262790508b8a89888888612f34565b61263190836136b4565b91505b995099975050505050505050565b600061265085858585612c18565b905080601554146126a35760405162461bcd60e51b815260206004820152601b60248201527f4d4c493a524e543a434f4d4d49544d454e545f4d49534d41544348000000000060448201526064016106c5565b6000601555949350505050565b6000806000806126d742601254600754601454600e54601354600854600a54600b54612fda565b60165491955093506126e990846136b4565b92506017549150601854905090919293565b6000833b8061270e5760009150506123f0565b846001600160a01b03168484604051612728929190613586565b600060405180830381855af49150503d8060008114612763576040519150601f19603f3d011682016040523d82523d6000602084013e612768565b606091505b50909695505050505050565b81600f6000828254612786919061370d565b90915550506005546127a2906001600160a01b031682846123ba565b6127e75760405162461bcd60e51b81526020600482015260166024820152751353124e91118e9514905394d1915497d1905253115160521b60448201526064016106c5565b6127ef612daf565b6109c75760405162461bcd60e51b815260206004820152601e60248201527f4d4c493a44463a494e53554646494349454e545f434f4c4c41544552414c000060448201526064016106c5565b60008060008060125442111561288c5760405162461bcd60e51b81526020600482015260166024820152754d4c493a434c3a5041594d454e545f49535f4c41544560501b60448201526064016106c5565b612894612544565b60006016819055939750919550935091506128af84866136b4565b9050816128bc84836136b4565b6128c691906136b4565b6005546128db906001600160a01b03166122e0565b600f546128e891906136b4565b6128f2919061370d565b600f81905550806010600082825461290a91906136b4565b9091555061291a90508383612dcf565b612922612bda565b5090919293565b816011600082825461293b919061370d565b9091555050600454612957906001600160a01b031682846123ba565b61299c5760405162461bcd60e51b81526020600482015260166024820152751353124e9490ce9514905394d1915497d1905253115160521b60448201526064016106c5565b6129a4612daf565b6109c75760405162461bcd60e51b815260206004820152601e60248201527f4d4c493a52433a494e53554646494349454e545f434f4c4c41544552414c000060448201526064016106c5565b60006001600160a01b038216612a405760405162461bcd60e51b815260206004820152601560248201527426a6249d23261d24a72b20a624a22fa622a72222a960591b60448201526064016106c5565b601354601254158015612a5257508015155b612a935760405162461bcd60e51b81526020600482015260126024820152714d4c493a464c3a4c4f414e5f41435449564560701b60448201526064016106c5565b600754600180546001600160a01b0319166001600160a01b038616179055612abb81426136b4565b601255600d5460148190556005549093506001600160a01b031683612adf826122e0565b1015612b2d5760405162461bcd60e51b815260206004820152601860248201527f4d4c493a464c3a57524f4e475f46554e445f414d4f554e54000000000000000060448201526064016106c5565b505050600f819055919050565b60006001600160a01b0383163b612b535750600061135e565b6060836001600160a01b031683604051612b6d9190613596565b6000604051808303816000865af19150503d8060008114612baa576040519150601f19603f3d011682016040523d82523d6000602084013e612baf565b606091505b509092509050818015610d1e575080511580610d1e575080806020019051810190610d1e9190613420565b60006006819055600781905560088190556009819055600a819055600b819055600e8190556012819055601381905560148190556017819055601855565b600084848484604051602001612c3194939291906135d1565b604051602081830303815290604052805190602001209050949350505050565b6000612c5b611d0c565b90508264496cebb80085836001600160a01b03166316a12d7a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612c9e57600080fd5b505afa158015612cb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cd6919061345b565b612ce090896136ee565b612cea91906136ee565b612cf491906136cc565b612cfe91906136b4565b6017819055508164496cebb80085836001600160a01b031663cc32d1766040518163ffffffff1660e01b815260040160206040518083038186803b158015612d4557600080fd5b505afa158015612d59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d7d919061345b565b612d8790896136ee565b612d9191906136ee565b612d9b91906136cc565b612da591906136b4565b6018555050505050565b6000612dc5601454600f54600d54600c54611fa8565b6011541015905090565b600154612ded906001600160a01b0316634046af2b60e01b84613029565b612e09578160106000828254612e0391906136b4565b90915550505b612e22612e14611d0c565b63a5a2760560e01b83613029565b6109c7578060106000828254612e3891906136b4565b90915550505050565b6000806000612e508686613138565b90506000612e78612e6983670de0b6b3a76400006136b4565b86670de0b6b3a7640000613153565b9050670de0b6b3a76400008111612eab5784612e94898b61370d565b612e9e91906136cc565b6000935093505050612f2a565b6000612ebf670de0b6b3a76400008361370d565b838a670de0b6b3a7640000612ed4868f6136ee565b612ede91906136cc565b612ee8919061370d565b612ef291906136ee565b612efc91906136cc565b9050612f098a89896131b5565b935083811015612f1a576000612f24565b612f24848261370d565b94505050505b9550959350505050565b6000838711612f4557506000612fd0565b6000620151806001612f57878b61370d565b612f61919061370d565b612f6b91906136cc565b612f769060016136b4565b612f8390620151806136ee565b9050612f9987612f9385896136b4565b836131b5565b612fa390836136b4565b9150670de0b6b3a7640000612fb888866136ee565b612fc291906136cc565b612fcc90836136b4565b9150505b9695505050505050565b600080612fea8888878c8a612e41565b909250905060018614612ffd5781612fff565b875b915061300f8b89878d8888612f34565b61301990826136b4565b9050995099975050505050505050565b600081613038575060016123f0565b60408051600481526024810182526020810180516001600160e01b03166001600160e01b03198716179052905160009182916001600160a01b0388169161307e91613596565b6000604051808303816000865af19150503d80600081146130bb576040519150601f19603f3d011682016040523d82523d6000602084013e6130c0565b606091505b50915091508115806130d457506020815114155b156130e4576000925050506123f0565b6000818060200190518101906130fa9190613289565b90506001600160a01b03811661311657600093505050506123f0565b60055461312d906001600160a01b031682876123ba565b979650505050505050565b60006301e1338061314983856136ee565b6123f091906136cc565b6000600183166131635781613165565b835b90505b60019290921c9182156123f0578161318085806136ee565b61318a91906136cc565b93506001831661319957613168565b816131a485836136ee565b6131ae91906136cc565b9050613168565b6000670de0b6b3a76400006131ca8484613138565b6131d490866136ee565b6123ed91906136cc565b60008083601f8401126131f057600080fd5b50813567ffffffffffffffff81111561320857600080fd5b6020830191508360208260051b850101111561322357600080fd5b9250929050565b60008083601f84011261323c57600080fd5b50813567ffffffffffffffff81111561325457600080fd5b60208301915083602082850101111561322357600080fd5b60006020828403121561327e57600080fd5b81356123f081613750565b60006020828403121561329b57600080fd5b81516123f081613750565b600080604083850312156132b957600080fd5b82356132c481613750565b915060208301356132d481613750565b809150509250929050565b6000806000604084860312156132f457600080fd5b83356132ff81613750565b9250602084013567ffffffffffffffff81111561331b57600080fd5b6133278682870161322a565b9497909650939450505050565b6000806040838503121561334757600080fd5b823561335281613750565b946020939093013593505050565b6000806000806060858703121561337657600080fd5b843561338181613750565b935060208501359250604085013567ffffffffffffffff8111156133a457600080fd5b6133b0878288016131de565b95989497509550505050565b6000806000806000608086880312156133d457600080fd5b85356133df81613750565b945060208601359350604086013567ffffffffffffffff81111561340257600080fd5b61340e888289016131de565b96999598509660600135949350505050565b60006020828403121561343257600080fd5b815180151581146123f057600080fd5b60006020828403121561345457600080fd5b5035919050565b60006020828403121561346d57600080fd5b5051919050565b6000806040838503121561348757600080fd5b8235915060208301356132d481613750565b6000806000604084860312156134ae57600080fd5b83359250602084013567ffffffffffffffff81111561331b57600080fd5b81835260006020808501808196508560051b810191508460005b878110156135505782840389528135601e1988360301811261350757600080fd5b8701803567ffffffffffffffff81111561352057600080fd5b80360389131561352f57600080fd5b61353c868289850161355d565b9a87019a95505050908401906001016134e6565b5091979650505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8183823760009101908152919050565b6000825160005b818110156135b7576020818601810151858301520161359d565b818111156135c6576000828501525b509190910192915050565b60018060a01b0385168152836020820152606060408201526000612fd06060830184866134cc565b85815260018060a01b038516602082015283604082015260806060820152600061312d6080830184866134cc565b60208082526012908201527113530e941493d513d0d3d317d4105554d15160721b604082015260600190565b838152604060208201526000611df460408301848661355d565b6000808335601e1984360301811261368457600080fd5b83018035915067ffffffffffffffff82111561369f57600080fd5b60200191503681900382131561322357600080fd5b600082198211156136c7576136c7613724565b500190565b6000826136e957634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561370857613708613724565b500290565b60008282101561371f5761371f613724565b500390565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6001600160a01b03811681146119d557600080fdfea264697066735822122024565e89cd77fea2f0418e44379d9ba6e10c7747e1f7e44e9e05d34842532dfe64736f6c63430008070033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103425760003560e01c80637c3a00fd116101b8578063ba5d307811610104578063d05951a0116100a2578063d8dfeb451161007c578063d8dfeb4514610634578063dac885611461063c578063e44b387514610654578063e920b1e11461065c57600080fd5b8063d05951a0146105fb578063d41ddc961461060e578063d784d4261461062157600080fd5b8063c3fbb6fd116100de578063c3fbb6fd146105cd578063c45a015514610368578063cc32d176146105e0578063ccc04484146105e857600080fd5b8063ba5d3078146105ac578063ba83276b146105b4578063bcead63e146105bc57600080fd5b8063a97d116111610171578063b69410de1161014b578063b69410de1461058c578063b86a513e14610594578063b96b5c991461059c578063b9b1f4e3146105a457600080fd5b8063a97d116114610560578063aabaecd614610568578063acb522b41461057957600080fd5b80637c3a00fd146105115780637df1f1b91461051957806387accaf11461052a5780638ffc92151461053d5780639e10320b14610545578063a06db7dc1461055857600080fd5b806345755dd6116102925780635eeb53b411610230578063712b772f1161020a578063712b772f146104e657806375a20676146104f957806377b3c55c146105015780637a0e6fa11461050957600080fd5b80635eeb53b4146104bc57806369458ba7146104cd578063700f5006146104d557600080fd5b806350acb4ee1161026c57806350acb4ee1461045b57806350f2012f1461046e5780635114cb52146104815780635c60da1b146104b457600080fd5b806345755dd61461040d57806347350e9f146104205780634eac42351461044857600080fd5b8063267f4ac3116102ff578063390d6855116102d9578063390d6855146103ce57806339ba9f86146103e15780633b99bcee146103f25780634003f34d1461040557600080fd5b8063267f4ac3146103ab5780632ead1098146103be57806330fea1ce146103c657600080fd5b806301daa38f146103475780630895326f146103515780630d49b38c146103685780630fe3d9b7146103885780631cc1cf46146103905780631f3f19ab14610398575b600080fd5b61034f61066f565b005b6013545b6040519081526020015b60405180910390f35b610370610715565b6040516001600160a01b03909116815260200161035f565b61034f610724565b600754610355565b61034f6103a636600461326c565b6107c7565b61034f6103b936600461326c565b61086c565b600b54610355565b600954610355565b61034f6103dc366004613474565b610908565b6005546001600160a01b0316610370565b61034f610400366004613499565b6109cb565b601254610355565b61035561041b366004613442565b610b3d565b61043361042e36600461326c565b610c12565b6040805192835260208301919091520161035f565b610355610456366004613442565b610cde565b61034f6104693660046133bc565b610d26565b61035561047c366004613442565b610f46565b61049461048f366004613442565b611002565b60408051948552602085019390935291830152606082015260800161035f565b610370611147565b6003546001600160a01b0316610370565b610494611151565b6002546001600160a01b0316610370565b6103556104f43660046132a6565b61116d565b600c54610355565b600a54610355565b610355611364565b600854610355565b6000546001600160a01b0316610370565b61034f610538366004613360565b6113a0565b600d54610355565b610355610553366004613442565b6114b5565b600654610355565b601654610355565b6004546001600160a01b0316610370565b61034f610587366004613360565b6114df565b601754610355565b600e54610355565b610494611590565b600f54610355565b601454610355565b601554610355565b6001546001600160a01b0316610370565b61034f6105db3660046132df565b61159e565b601854610355565b6103556105f6366004613474565b611643565b610494610609366004613442565b611752565b61034f61061c366004613474565b61188b565b61034f61062f36600461326c565b61194c565b601154610355565b6106446119d8565b604051901515815260200161035f565b601054610355565b61035561066a366004613334565b611ac2565b6002546001600160a01b031633146106ce5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a41423a4e4f545f50454e44494e475f424f52524f57455200000000000060448201526064015b60405180910390fd5b600280546001600160a01b031990811690915560008054339216821781556040517f29bac0ac2b15405bfcc160bb74b6ae7a559b7674ce33db80785ada73e38204d29190a2565b600061071f611c6a565b905090565b6003546001600160a01b0316331461077e5760405162461bcd60e51b815260206004820152601860248201527f4d4c3a414c3a4e4f545f50454e44494e475f4c454e444552000000000000000060448201526064016106c5565b600380546001600160a01b031990811690915560018054339216821790556040517fd6165838d2e3db87aa1002b548048673fc6427eefbd1b914e100f3a0deae23e390600090a2565b6000546001600160a01b031633146108175760405162461bcd60e51b815260206004820152601360248201527226a61d29a8211d2727aa2fa127a92927aba2a960691b60448201526064016106c5565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f10f06072822ef73860fedb88933f968d20bb4aadce8a8d360d1124cb6ce1e0b2906020015b60405180910390a150565b6001546001600160a01b031633146108ba5760405162461bcd60e51b815260206004820152601160248201527026a61d29a8261d2727aa2fa622a72222a960791b60448201526064016106c5565b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527fa3ab02442c80a4102475683f16513c9139a89142be9db9804edfcfbb379fc49290602001610861565b6109106119d8565b1561092d5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b0316331461097a5760405162461bcd60e51b815260206004820152601060248201526f26a61d21a31d2727aa2fa622a72222a960811b60448201526064016106c5565b806001600160a01b03167f6bd56533ce1c8ea03f7b858ac441b5a86d140a793a7c9e3faecbbe517c2c8791836040516109b591815260200190565b60405180910390a26109c78282611c99565b5050565b6109d3611d0c565b6001600160a01b031663ec9a93686040518163ffffffff1660e01b815260040160206040518083038186803b158015610a0b57600080fd5b505afa158015610a1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a439190613289565b6001600160a01b0316336001600160a01b031614610a945760405162461bcd60e51b815260206004820152600e60248201526d26a61d2a9d2727aa2fa0a226a4a760911b60448201526064016106c5565b7faaaa7ee6b0c2f4ee1fa7312c7d5b3623a434da5a1a9ce3cb6e629caa23454ab6838383604051610ac793929190613653565b60405180910390a1610ad7611c6a565b6001600160a01b031663fe69f7088484846040518463ffffffff1660e01b8152600401610b0693929190613653565b600060405180830381600087803b158015610b2057600080fd5b505af1158015610b34573d6000803e3d6000fd5b50505050505050565b6000610b476119d8565b15610b645760405162461bcd60e51b81526004016106c590613627565b811580610b845750600554610b84906001600160a01b0316333085611d86565b610bd05760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a52463a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b7f278e51d3323fbf18b9fb8df3f8b97e31b145bc1146c52e764cf4aa1bfc4ba17d610bf9611dfd565b60405181815290925060200160405180910390a1919050565b600080610c1d6119d8565b15610c3a5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b03163314610c865760405162461bcd60e51b815260206004820152600f60248201526e26a61d291d2727aa2fa622a72222a960891b60448201526064016106c5565b610c8f83611e33565b60408051838152602081018390529294509092506001600160a01b038516917f027e623aab0b174da270ff529cad1c54f09182651e07437d2ac557929b9e5b49910160405180910390a2915091565b600080610d0060145484600f54610cf5919061370d565b600d54600c54611fa8565b601154909150808211610d14576000610d1e565b610d1e818361370d565b949350505050565b610d2e6119d8565b15610d4b5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b0316338114610d9a5760405162461bcd60e51b815260206004820152601160248201527026a61d20a72a1d2727aa2fa622a72222a960791b60448201526064016106c5565b6005546001600160a01b0316821580610dba5750610dba81333086611d86565b610e065760405162461bcd60e51b815260206004820152601b60248201527f4d4c3a414e543a5452414e534645525f46524f4d5f4641494c4544000000000060448201526064016106c5565b7f7150c332bd889236b6ab42cc34f0853631ceb58827f58a8697b682f13e390a8c610e3388888888611fe1565b88888888604051610e489594939291906135f9565b60405180910390a17ffe9a32948c4b8ec5c8a8eddeacd3f3621458e8bde95b725b625e5c8f4f2cb54d601754601854604051610e8e929190918252602082015260400190565b60405180910390a16000610ea1826122e0565b90508015610f3c57826001600160a01b03167ff505854d1244de20a434e0eca67ec8de6d69504f7f85594c61102ab4d9a278f382604051610ee491815260200190565b60405180910390a2610ef78284836123ba565b610f3c5760405162461bcd60e51b815260206004820152601660248201527513530e9053950e9514905394d1915497d1905253115160521b60448201526064016106c5565b5050505050505050565b6000610f506119d8565b15610f6d5760405162461bcd60e51b81526004016106c590613627565b811580610f8d5750600454610f8d906001600160a01b0316333085611d86565b610fd95760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a50433a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b7f437d44b2c697fb69e2b2f25f57fd844e376c25ed28ed5a9c4be88aa1e5c87d12610bf96123f7565b6000806000806000600f54905060008614806110315750600554611031906001600160a01b0316333089611d86565b61107d5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a4d503a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b611085612423565b600054939850919650945092506001600160a01b03163314806110aa575080600f5410155b6110f65760405162461bcd60e51b815260206004820152601960248201527f4d4c3a4d503a43414e4e4f545f5553455f4452415741424c450000000000000060448201526064016106c5565b6040805186815260208101869052908101849052606081018390527f95c4acf903eb698cf367efaaf79a8a58fb4554fcd8503b62af9f9c1b68b59e1e906080015b60405180910390a1509193509193565b600061071f61251a565b60008060008061115f612544565b929791965094509092509050565b60006111776119d8565b156111945760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b03163314806111b757506001546001600160a01b031633145b6111f15760405162461bcd60e51b815260206004820152600b60248201526a09874a6749c9ebe82aaa8960ab1b60448201526064016106c5565b6005546001600160a01b0384811691161480159061121d57506004546001600160a01b03848116911614155b61125d5760405162461bcd60e51b8152602060048201526011602482015270261d299d24a72b20a624a22faa27a5a2a760791b60448201526064016106c5565b6040516370a0823160e01b81523060048201526001600160a01b0380841691908516907ff1f6a55e7ad487ac8dd8e1d4517348d3b410a7a0bc405ef87b09078dc51b23b69082906370a082319060240160206040518083038186803b1580156112c557600080fd5b505afa1580156112d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112fd919061345b565b60405181815290945060200160405180910390a361131c8383836123ba565b61135e5760405162461bcd60e51b8152602060048201526013602482015272130e94ce9514905394d1915497d19052531151606a1b60448201526064016106c5565b92915050565b60008061137b601454600f54600d54600c54611fa8565b60115490915081811161138f576000611399565b611399828261370d565b9250505090565b6113a86119d8565b156113c55760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146114155760405162461bcd60e51b815260206004820152601360248201527226a61d28272a1d2727aa2fa127a92927aba2a960691b60448201526064016106c5565b428310156114655760405162461bcd60e51b815260206004820152601760248201527f4d4c3a504e543a494e56414c49445f444541444c494e4500000000000000000060448201526064016106c5565b7ff94d2f0322894aaf1bce14561461a8b8b6c9b11a77bbe80f20b804da8a95e4b7611492858585856125ab565b858585856040516114a79594939291906135f9565b60405180910390a150505050565b60006114d882600754601454600e54600854601354601254600a54600b546125d3565b5092915050565b6114e76119d8565b156115045760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633148061152757506001546001600160a01b031633145b6115635760405162461bcd60e51b815260206004820152600d60248201526c09874a49ca8749c9ebe82aaa89609b1b60448201526064016106c5565b7f47244a449377da5fd10e98d86d118dee442e842fc34f05179c973cfcff6acba761149285858585612642565b60008060008061115f6126b0565b6115a6611c6a565b6001600160a01b0316336001600160a01b0316146115f95760405162461bcd60e51b815260206004820152601060248201526f4d4c3a4d3a4e4f545f464143544f525960801b60448201526064016106c5565b6116048383836126fb565b61163e5760405162461bcd60e51b815260206004820152600b60248201526a13530e934e91905253115160aa1b60448201526064016106c5565b505050565b600061164d6119d8565b1561166a5760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146116b95760405162461bcd60e51b815260206004820152601260248201527126a61d22231d2727aa2fa127a92927aba2a960711b60448201526064016106c5565b816001600160a01b03167f7578fe8c4d9f6fc38fdad20d219b0ce47d38bbf8a72bdb26867809f24119363d846040516116f491815260200190565b60405180910390a2600061170784610cde565b9050801561174857600454600090611727906001600160a01b03166122e0565b905061174481831161173a576000610f46565b61047c828461370d565b9250505b6114d88484612774565b6000806000806000600f54905060008614806117815750600554611781906001600160a01b0316333089611d86565b6117cd5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a434c3a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b6117d561283b565b600054939850919650945092506001600160a01b03163314806117fa575080600f5410155b6118465760405162461bcd60e51b815260206004820152601960248201527f4d4c3a434c3a43414e4e4f545f5553455f4452415741424c450000000000000060448201526064016106c5565b6040805186815260208101869052908101849052606081018390527f6d5b31efac20a15ed5b9e27e38cf9ebcc3ffb6d64feb827a35ef84a607e8dfaf90608001611137565b6118936119d8565b156118b05760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146118ff5760405162461bcd60e51b815260206004820152601260248201527126a61d29219d2727aa2fa127a92927aba2a960711b60448201526064016106c5565b806001600160a01b03167f97b446ee2df422b7273fe6d658674835f9de3319d131c229f9a2f8ed62a761938360405161193a91815260200190565b60405180910390a26109c78282612929565b611954611c6a565b6001600160a01b0316336001600160a01b0316146119a85760405162461bcd60e51b81526020600482015260116024820152704d4c3a53493a4e4f545f464143544f525960781b60448201526064016106c5565b6001600160a01b03167f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b50565b60006119e2611c6a565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a1a57600080fd5b505afa158015611a2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a529190613289565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8a57600080fd5b505afa158015611a9e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071f9190613420565b6000611acc6119d8565b15611ae95760405162461bcd60e51b81526004016106c590613627565b6005546001600160a01b0316821580611b095750611b0981333086611d86565b611b555760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a464c3a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b601254611baf57836001600160a01b03167fcd909ec339185c4598a4096e174308fbdf136d117f230960f873a2f2e81f63af611b90866129f0565b6012546040805183815260208101929092529195500160405180910390a25b6000611bba826122e0565b6001549091506001600160a01b03168115611c6157806001600160a01b03167ff505854d1244de20a434e0eca67ec8de6d69504f7f85594c61102ab4d9a278f383604051611c0a91815260200190565b60405180910390a2611c1d8382846123ba565b611c615760405162461bcd60e51b815260206004820152601560248201527413530e91930e9514905394d1915497d19052531151605a1b60448201526064016106c5565b50505092915050565b6000611c947f7a45a402e4cb6e08ebc196f20f66d5d30e67285a2a8aa80503fa409e727a4af15490565b919050565b8160106000828254611cab919061370d565b9091555050600554611cc7906001600160a01b031682846123ba565b6109c75760405162461bcd60e51b81526020600482015260166024820152751353124e90d18e9514905394d1915497d1905253115160521b60448201526064016106c5565b6000611d16611c6a565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d4e57600080fd5b505afa158015611d62573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071f9190613289565b6040516001600160a01b0380851660248301528316604482015260648101829052600090611df49086906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612b3a565b95945050505050565b600554600090611e15906001600160a01b03166122e0565b905080600f6000828254611e2991906136b4565b9250508190555090565b60125460009081908015801590611e555750600654611e5290826136b4565b42115b611e985760405162461bcd60e51b81526020600482015260146024820152731353124e948e9393d517d25397d111519055531560621b60448201526064016106c5565b611ea0612bda565b600060118190556010819055600f8190556004546001600160a01b031690611ec7826122e0565b9450841480611edc5750611edc8186866123ba565b611f285760405162461bcd60e51b815260206004820152601760248201527f4d4c493a523a435f5452414e534645525f4641494c454400000000000000000060448201526064016106c5565b6005546001600160a01b03166000611f3f826122e0565b9450841480611f545750611f548187866123ba565b611fa05760405162461bcd60e51b815260206004820152601760248201527f4d4c493a523a465f5452414e534645525f4641494c454400000000000000000060448201526064016106c5565b505050915091565b600083851115611fd65782611fbd858761370d565b611fc790846136ee565b611fd191906136cc565b611df4565b506000949350505050565b6000611fef85858585612c18565b905080601554146120425760405162461bcd60e51b815260206004820152601b60248201527f4d4c493a414e543a434f4d4d49544d454e545f4d49534d41544348000000000060448201526064016106c5565b6001600160a01b0385163b6120995760405162461bcd60e51b815260206004820152601a60248201527f4d4c493a414e543a494e56414c49445f524546494e414e43455200000000000060448201526064016106c5565b834211156120e95760405162461bcd60e51b815260206004820152601a60248201527f4d4c493a414e543a455850495245445f434f4d4d49544d454e5400000000000060448201526064016106c5565b600060075490506000806121124284601454600e54600854601354601254600a54600b546125d3565b91509150816016600082825461212891906136b4565b90915550506000601581905585905b818110156122085760008a6001600160a01b031689898481811061215d5761215d61373a565b905060200281019061216f919061366d565b60405161217d929190613586565b600060405180830381855af49150503d80600081146121b8576040519150601f19603f3d011682016040523d82523d6000602084013e6121bd565b606091505b50509050806121ff5760405162461bcd60e51b815260206004820152600e60248201526d1353124e9053950e91905253115160921b60448201526064016106c5565b50600101612137565b5060006013548561221991906136ee565b8360175461222791906136ee565b61223191906136cc565b905060006013548661224391906136ee565b8460185461225191906136ee565b61225b91906136cc565b60075490915061226b81426136b4565b60125560145461227d90828585612c51565b612285612daf565b6122d15760405162461bcd60e51b815260206004820152601f60248201527f4d4c493a414e543a494e53554646494349454e545f434f4c4c41544552414c0060448201526064016106c5565b50505050505050949350505050565b6005546000906001600160a01b038381169116146122ff57600061230f565b600f5460105461230f91906136b4565b6004546001600160a01b0384811691161461232b57600061232f565b6011545b6040516370a0823160e01b81523060048201526001600160a01b038516906370a082319060240160206040518083038186803b15801561236e57600080fd5b505afa158015612382573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a6919061345b565b6123b0919061370d565b61135e919061370d565b6040516001600160a01b0383166024820152604481018290526000906123ed90859063a9059cbb60e01b90606401611dbd565b90505b9392505050565b60045460009061240f906001600160a01b03166122e0565b90508060116000828254611e2991906136b4565b6000806000806124316126b0565b600060168190559397509195509350915061244c84866136b4565b90508161245984836136b4565b61246391906136b4565b600554612478906001600160a01b03166122e0565b600f5461248591906136b4565b61248f919061370d565b600f8190555080601060008282546124a791906136b4565b909155506124b790508383612dcf565b60135460018114156124d0576124cb612bda565b612512565b600754601260008282546124e491906136b4565b9250508190555085601460008282546124fd919061370d565b9091555061250e905060018261370d565b6013555b505090919293565b6000611c947f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b600080600080601654670de0b6b3a764000060095460145496508661256991906136ee565b61257391906136cc565b61257d91906136b4565b601354601754919450906125929082906136ee565b9250806018546125a291906136ee565b91505090919293565b6000816125b95760006125c5565b6125c585858585612c18565b601581905595945050505050565b600080846125e18b8d6136b4565b10156125f257506000905080612634565b6125fc8a8661370d565b612606908c61370d565b9050612615898989848a612e41565b925061262790508b8a89888888612f34565b61263190836136b4565b91505b995099975050505050505050565b600061265085858585612c18565b905080601554146126a35760405162461bcd60e51b815260206004820152601b60248201527f4d4c493a524e543a434f4d4d49544d454e545f4d49534d41544348000000000060448201526064016106c5565b6000601555949350505050565b6000806000806126d742601254600754601454600e54601354600854600a54600b54612fda565b60165491955093506126e990846136b4565b92506017549150601854905090919293565b6000833b8061270e5760009150506123f0565b846001600160a01b03168484604051612728929190613586565b600060405180830381855af49150503d8060008114612763576040519150601f19603f3d011682016040523d82523d6000602084013e612768565b606091505b50909695505050505050565b81600f6000828254612786919061370d565b90915550506005546127a2906001600160a01b031682846123ba565b6127e75760405162461bcd60e51b81526020600482015260166024820152751353124e91118e9514905394d1915497d1905253115160521b60448201526064016106c5565b6127ef612daf565b6109c75760405162461bcd60e51b815260206004820152601e60248201527f4d4c493a44463a494e53554646494349454e545f434f4c4c41544552414c000060448201526064016106c5565b60008060008060125442111561288c5760405162461bcd60e51b81526020600482015260166024820152754d4c493a434c3a5041594d454e545f49535f4c41544560501b60448201526064016106c5565b612894612544565b60006016819055939750919550935091506128af84866136b4565b9050816128bc84836136b4565b6128c691906136b4565b6005546128db906001600160a01b03166122e0565b600f546128e891906136b4565b6128f2919061370d565b600f81905550806010600082825461290a91906136b4565b9091555061291a90508383612dcf565b612922612bda565b5090919293565b816011600082825461293b919061370d565b9091555050600454612957906001600160a01b031682846123ba565b61299c5760405162461bcd60e51b81526020600482015260166024820152751353124e9490ce9514905394d1915497d1905253115160521b60448201526064016106c5565b6129a4612daf565b6109c75760405162461bcd60e51b815260206004820152601e60248201527f4d4c493a52433a494e53554646494349454e545f434f4c4c41544552414c000060448201526064016106c5565b60006001600160a01b038216612a405760405162461bcd60e51b815260206004820152601560248201527426a6249d23261d24a72b20a624a22fa622a72222a960591b60448201526064016106c5565b601354601254158015612a5257508015155b612a935760405162461bcd60e51b81526020600482015260126024820152714d4c493a464c3a4c4f414e5f41435449564560701b60448201526064016106c5565b600754600180546001600160a01b0319166001600160a01b038616179055612abb81426136b4565b601255600d5460148190556005549093506001600160a01b031683612adf826122e0565b1015612b2d5760405162461bcd60e51b815260206004820152601860248201527f4d4c493a464c3a57524f4e475f46554e445f414d4f554e54000000000000000060448201526064016106c5565b505050600f819055919050565b60006001600160a01b0383163b612b535750600061135e565b6060836001600160a01b031683604051612b6d9190613596565b6000604051808303816000865af19150503d8060008114612baa576040519150601f19603f3d011682016040523d82523d6000602084013e612baf565b606091505b509092509050818015610d1e575080511580610d1e575080806020019051810190610d1e9190613420565b60006006819055600781905560088190556009819055600a819055600b819055600e8190556012819055601381905560148190556017819055601855565b600084848484604051602001612c3194939291906135d1565b604051602081830303815290604052805190602001209050949350505050565b6000612c5b611d0c565b90508264496cebb80085836001600160a01b03166316a12d7a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612c9e57600080fd5b505afa158015612cb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cd6919061345b565b612ce090896136ee565b612cea91906136ee565b612cf491906136cc565b612cfe91906136b4565b6017819055508164496cebb80085836001600160a01b031663cc32d1766040518163ffffffff1660e01b815260040160206040518083038186803b158015612d4557600080fd5b505afa158015612d59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d7d919061345b565b612d8790896136ee565b612d9191906136ee565b612d9b91906136cc565b612da591906136b4565b6018555050505050565b6000612dc5601454600f54600d54600c54611fa8565b6011541015905090565b600154612ded906001600160a01b0316634046af2b60e01b84613029565b612e09578160106000828254612e0391906136b4565b90915550505b612e22612e14611d0c565b63a5a2760560e01b83613029565b6109c7578060106000828254612e3891906136b4565b90915550505050565b6000806000612e508686613138565b90506000612e78612e6983670de0b6b3a76400006136b4565b86670de0b6b3a7640000613153565b9050670de0b6b3a76400008111612eab5784612e94898b61370d565b612e9e91906136cc565b6000935093505050612f2a565b6000612ebf670de0b6b3a76400008361370d565b838a670de0b6b3a7640000612ed4868f6136ee565b612ede91906136cc565b612ee8919061370d565b612ef291906136ee565b612efc91906136cc565b9050612f098a89896131b5565b935083811015612f1a576000612f24565b612f24848261370d565b94505050505b9550959350505050565b6000838711612f4557506000612fd0565b6000620151806001612f57878b61370d565b612f61919061370d565b612f6b91906136cc565b612f769060016136b4565b612f8390620151806136ee565b9050612f9987612f9385896136b4565b836131b5565b612fa390836136b4565b9150670de0b6b3a7640000612fb888866136ee565b612fc291906136cc565b612fcc90836136b4565b9150505b9695505050505050565b600080612fea8888878c8a612e41565b909250905060018614612ffd5781612fff565b875b915061300f8b89878d8888612f34565b61301990826136b4565b9050995099975050505050505050565b600081613038575060016123f0565b60408051600481526024810182526020810180516001600160e01b03166001600160e01b03198716179052905160009182916001600160a01b0388169161307e91613596565b6000604051808303816000865af19150503d80600081146130bb576040519150601f19603f3d011682016040523d82523d6000602084013e6130c0565b606091505b50915091508115806130d457506020815114155b156130e4576000925050506123f0565b6000818060200190518101906130fa9190613289565b90506001600160a01b03811661311657600093505050506123f0565b60055461312d906001600160a01b031682876123ba565b979650505050505050565b60006301e1338061314983856136ee565b6123f091906136cc565b6000600183166131635781613165565b835b90505b60019290921c9182156123f0578161318085806136ee565b61318a91906136cc565b93506001831661319957613168565b816131a485836136ee565b6131ae91906136cc565b9050613168565b6000670de0b6b3a76400006131ca8484613138565b6131d490866136ee565b6123ed91906136cc565b60008083601f8401126131f057600080fd5b50813567ffffffffffffffff81111561320857600080fd5b6020830191508360208260051b850101111561322357600080fd5b9250929050565b60008083601f84011261323c57600080fd5b50813567ffffffffffffffff81111561325457600080fd5b60208301915083602082850101111561322357600080fd5b60006020828403121561327e57600080fd5b81356123f081613750565b60006020828403121561329b57600080fd5b81516123f081613750565b600080604083850312156132b957600080fd5b82356132c481613750565b915060208301356132d481613750565b809150509250929050565b6000806000604084860312156132f457600080fd5b83356132ff81613750565b9250602084013567ffffffffffffffff81111561331b57600080fd5b6133278682870161322a565b9497909650939450505050565b6000806040838503121561334757600080fd5b823561335281613750565b946020939093013593505050565b6000806000806060858703121561337657600080fd5b843561338181613750565b935060208501359250604085013567ffffffffffffffff8111156133a457600080fd5b6133b0878288016131de565b95989497509550505050565b6000806000806000608086880312156133d457600080fd5b85356133df81613750565b945060208601359350604086013567ffffffffffffffff81111561340257600080fd5b61340e888289016131de565b96999598509660600135949350505050565b60006020828403121561343257600080fd5b815180151581146123f057600080fd5b60006020828403121561345457600080fd5b5035919050565b60006020828403121561346d57600080fd5b5051919050565b6000806040838503121561348757600080fd5b8235915060208301356132d481613750565b6000806000604084860312156134ae57600080fd5b83359250602084013567ffffffffffffffff81111561331b57600080fd5b81835260006020808501808196508560051b810191508460005b878110156135505782840389528135601e1988360301811261350757600080fd5b8701803567ffffffffffffffff81111561352057600080fd5b80360389131561352f57600080fd5b61353c868289850161355d565b9a87019a95505050908401906001016134e6565b5091979650505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8183823760009101908152919050565b6000825160005b818110156135b7576020818601810151858301520161359d565b818111156135c6576000828501525b509190910192915050565b60018060a01b0385168152836020820152606060408201526000612fd06060830184866134cc565b85815260018060a01b038516602082015283604082015260806060820152600061312d6080830184866134cc565b60208082526012908201527113530e941493d513d0d3d317d4105554d15160721b604082015260600190565b838152604060208201526000611df460408301848661355d565b6000808335601e1984360301811261368457600080fd5b83018035915067ffffffffffffffff82111561369f57600080fd5b60200191503681900382131561322357600080fd5b600082198211156136c7576136c7613724565b500190565b6000826136e957634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561370857613708613724565b500290565b60008282101561371f5761371f613724565b500390565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6001600160a01b03811681146119d557600080fdfea264697066735822122024565e89cd77fea2f0418e44379d9ba6e10c7747e1f7e44e9e05d34842532dfe64736f6c63430008070033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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