ETH Price: $3,271.14 (+2.00%)

Contract

0xe7Bd3cc389B2182E6eC350fa9c90670dD76c061c
 

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: BUSL-1.1
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 { MapleProxiedInternals } from "../modules/maple-proxy-factory/contracts/MapleProxiedInternals.sol";

import { IMapleLoan }           from "./interfaces/IMapleLoan.sol";
import { IMapleLoanFeeManager } from "./interfaces/IMapleLoanFeeManager.sol";

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

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

/*

    ███╗   ███╗ █████╗ ██████╗ ██╗     ███████╗    ██╗      ██████╗  █████╗ ███╗   ██╗    ██╗   ██╗██╗  ██╗
    ████╗ ████║██╔══██╗██╔══██╗██║     ██╔════╝    ██║     ██╔═══██╗██╔══██╗████╗  ██║    ██║   ██║██║  ██║
    ██╔████╔██║███████║██████╔╝██║     █████╗      ██║     ██║   ██║███████║██╔██╗ ██║    ██║   ██║███████║
    ██║╚██╔╝██║██╔══██║██╔═══╝ ██║     ██╔══╝      ██║     ██║   ██║██╔══██║██║╚██╗██║    ╚██╗ ██╔╝╚════██║
    ██║ ╚═╝ ██║██║  ██║██║     ███████╗███████╗    ███████╗╚██████╔╝██║  ██║██║ ╚████║     ╚████╔╝      ██║
    ╚═╝     ╚═╝╚═╝  ╚═╝╚═╝     ╚══════╝╚══════╝    ╚══════╝ ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═══╝      ╚═══╝       ╚═╝

*/

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

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

    modifier limitDrawableUse() {
        if (msg.sender == _borrower) {
            _;
            return;
        }

        uint256 drawableFundsBeforePayment = _drawableFunds;

        _;

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

    // NOTE: The following functions already check for paused state in the poolManager/loanManager, therefore no need to check here.
    // * acceptNewTerms
    // * fundLoan
    // * impairLoan
    // * removeLoanImpairment
    // * repossess
    // * setPendingLender -> Not implemented
    modifier whenProtocolNotPaused() {
        require(!IMapleGlobalsLike(globals()).protocolPaused(), "L: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 whenProtocolNotPaused {
        require(msg.sender == IMapleGlobalsLike(globals()).securityAdmin(), "ML:U:NOT_SECURITY_ADMIN");

        emit Upgraded(toVersion_, arguments_);

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

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

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

        _pendingBorrower = address(0);

        emit BorrowerAccepted(_borrower = msg.sender);
    }

    function closeLoan(uint256 amount_) external override limitDrawableUse whenProtocolNotPaused returns (uint256 principal_, uint256 interest_, uint256 fees_) {
        // The amount specified is an optional amount to be transferred from the caller, as a convenience for EOAs.
        // NOTE: FUNDS SHOULD NOT BE TRANSFERRED TO THIS CONTRACT NON-ATOMICALLY. IF THEY ARE, THE BALANCE MAY BE STOLEN USING `skim`.
        require(amount_ == uint256(0) || ERC20Helper.transferFrom(_fundsAsset, msg.sender, address(this), amount_), "ML:CL:TRANSFER_FROM_FAILED");

        uint256 paymentDueDate_ = _nextPaymentDueDate;

        require(block.timestamp <= paymentDueDate_, "ML:CL:PAYMENT_IS_LATE");

        _handleImpairment();

        ( principal_, interest_, ) = getClosingPaymentBreakdown();

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

        fees_ = _handleServiceFeePayment(_paymentsRemaining);

        _clearLoanAccounting();

        emit LoanClosed(principal_, interest_, fees_);

        require(ERC20Helper.transfer(_fundsAsset, _lender, principalAndInterest), "ML:MP:TRANSFER_FAILED");

        ILenderLike(_lender).claim(principal_, interest_, paymentDueDate_, 0);

        emit FundsClaimed(principalAndInterest, _lender);
    }

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

        _drawableFunds -= amount_;

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

    function makePayment(uint256 amount_) external override limitDrawableUse whenProtocolNotPaused returns (uint256 principal_, uint256 interest_, uint256 fees_) {
        // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs.
        // NOTE: FUNDS SHOULD NOT BE TRANSFERRED TO THIS CONTRACT NON-ATOMICALLY. IF THEY ARE, THE BALANCE MAY BE STOLEN USING `skim`.
        require(amount_ == uint256(0) || ERC20Helper.transferFrom(_fundsAsset, msg.sender, address(this), amount_), "ML:MP:TRANSFER_FROM_FAILED");

        _handleImpairment();

        ( principal_, interest_, ) = 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;

        fees_ = _handleServiceFeePayment(1);

        uint256 paymentsRemaining_      = _paymentsRemaining;
        uint256 previousPaymentDueDate_ = _nextPaymentDueDate;
        uint256 nextPaymentDueDate_;

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

        emit PaymentMade(principal_, interest_, fees_);

        require(ERC20Helper.transfer(_fundsAsset, _lender, principalAndInterest), "ML:MP:TRANSFER_FAILED");

        ILenderLike(_lender).claim(principal_, interest_, previousPaymentDueDate_, nextPaymentDueDate_);

        emit FundsClaimed(principalAndInterest, _lender);
    }

    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.
        // NOTE: FUNDS SHOULD NOT BE TRANSFERRED TO THIS CONTRACT NON-ATOMICALLY. IF THEY ARE, THE BALANCE MAY BE STOLEN USING `skim`.
        require(
            amount_ == uint256(0) || ERC20Helper.transferFrom(_collateralAsset, msg.sender, address(this), amount_),
            "ML:PC:TRANSFER_FROM_FAILED"
        );

        _collateral += (collateralPosted_ = getUnaccountedAmount(_collateralAsset));

        emit CollateralPosted(collateralPosted_);
    }

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

        emit NewTermsProposed(
            refinanceCommitment_ = _refinanceCommitment = calls_.length > uint256(0)
                ? _getRefinanceCommitment(refinancer_, deadline_, calls_)
                : bytes32(0),
            refinancer_,
            deadline_,
            calls_
        );
    }

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

        emit CollateralRemoved(amount_, destination_);

        _collateral -= amount_;

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

    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.
        // NOTE: FUNDS SHOULD NOT BE TRANSFERRED TO THIS CONTRACT NON-ATOMICALLY. IF THEY ARE, THE BALANCE MAY BE STOLEN USING `skim`.
        require(amount_ == uint256(0) || ERC20Helper.transferFrom(_fundsAsset, msg.sender, address(this), amount_), "ML:RF:TRANSFER_FROM_FAILED");

        _drawableFunds += (fundsReturned_ = getUnaccountedAmount(_fundsAsset));

        emit FundsReturned(fundsReturned_);
    }

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

        emit PendingBorrowerSet(_pendingBorrower = pendingBorrower_);
    }

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

    function acceptLender() external override whenProtocolNotPaused {
        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_) external override returns (bytes32 refinanceCommitment_) {
        require(msg.sender == _lender, "ML:ANT:NOT_LENDER");

        // NOTE: A zero refinancer address and/or empty calls array will never (probabilistically) match a refinance commitment in storage.
        require(
            _refinanceCommitment == (refinanceCommitment_ = _getRefinanceCommitment(refinancer_, deadline_, calls_)),
            "ML:ANT:COMMITMENT_MISMATCH"
        );

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

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

        uint256 paymentInterval_           = _paymentInterval;
        uint256 nextPaymentDueDate_        = _nextPaymentDueDate;
        uint256 previousPrincipalRequested = _principalRequested;

        uint256 timeSinceLastDueDate_ = block.timestamp + paymentInterval_ < nextPaymentDueDate_ ? 0 : block.timestamp - (nextPaymentDueDate_ - paymentInterval_);

        // Not ideal for checks-effects-interactions, but the feeManager is a trusted contract and it's needed to save the fee before refinance.
        IMapleLoanFeeManager feeManager_ = IMapleLoanFeeManager(_feeManager);
        feeManager_.updateRefinanceServiceFees(previousPrincipalRequested, timeSinceLastDueDate_);

        // Get the amount of interest owed since the last payment due date, as well as the time since the last due date
        uint256 proRataInterest = getRefinanceInterest(block.timestamp);

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

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

        emit NewTermsAccepted(refinanceCommitment_, refinancer_, deadline_, calls_);

        address fundsAsset_         = _fundsAsset;
        uint256 principalRequested_ = _principalRequested;
        paymentInterval_            = _paymentInterval;

        // 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.
        _nextPaymentDueDate = block.timestamp + paymentInterval_;

        _handleImpairment();

        // Update Platform Fees and pay originations.
        feeManager_.updatePlatformServiceFee(principalRequested_, paymentInterval_);

        _drawableFunds -= feeManager_.payOriginationFees(fundsAsset_, principalRequested_);

        // Ensure that collateral is maintained after changes made.
        require(_isCollateralMaintained(),                       "ML:ANT:INSUFFICIENT_COLLATERAL");
        require(getUnaccountedAmount(fundsAsset_) == uint256(0), "ML:ANT:UNEXPECTED_FUNDS");
    }

    function fundLoan(address lender_) external override returns (uint256 fundsLent_) {
        require((_lender = lender_) != address(0), "ML:FL:INVALID_LENDER");

        address loanManagerFactory_ = ILenderLike(lender_).factory();

        require(IMapleGlobalsLike(globals()).isFactory("LOAN_MANAGER", loanManagerFactory_), "ML:FL:INVALID_FACTORY");
        require(IMapleProxyFactoryLike(loanManagerFactory_).isInstance(lender_),             "ML:FL:INVALID_INSTANCE");

        // 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)), "ML:FL:LOAN_ACTIVE");

        address fundsAsset_         = _fundsAsset;
        uint256 paymentInterval_    = _paymentInterval;
        uint256 principalRequested_ = _principalRequested;

        require(ERC20Helper.approve(fundsAsset_, _feeManager, type(uint256).max), "ML:FL:APPROVE_FAIL");

        // Saves the platform service fee rate for future payments.
        IMapleLoanFeeManager(_feeManager).updatePlatformServiceFee(principalRequested_, paymentInterval_);

        uint256 originationFees_ = IMapleLoanFeeManager(_feeManager).payOriginationFees(fundsAsset_, principalRequested_);

        _drawableFunds = principalRequested_ - originationFees_;

        require(getUnaccountedAmount(fundsAsset_) == uint256(0), "ML:FL:UNEXPECTED_FUNDS");

        emit Funded(
            lender_,
            fundsLent_ = _principal = principalRequested_,
            _nextPaymentDueDate = block.timestamp + paymentInterval_
        );
    }

    function removeLoanImpairment() external override {
        uint256 originalNextPaymentDueDate_ = _originalNextPaymentDueDate;

        require(msg.sender == _lender,                          "ML:RLI:NOT_LENDER");
        require(originalNextPaymentDueDate_ != 0,               "ML:RLI:NOT_IMPAIRED");
        require(block.timestamp <= originalNextPaymentDueDate_, "ML:RLI:PAST_DATE");

        _nextPaymentDueDate = originalNextPaymentDueDate_;
        delete _originalNextPaymentDueDate;

        emit NextPaymentDueDateRestored(originalNextPaymentDueDate_);
    }

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

        uint256 nextPaymentDueDate_ = _nextPaymentDueDate;

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

        _clearLoanAccounting();

        // Uniquely in `_repossess`, stop accounting for all funds so that they can be swept.
        _collateral    = 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_),
            "ML: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_),
            "ML:R:F_TRANSFER_FAILED"
        );

        emit Repossessed(collateralRepossessed_, fundsRepossessed_, destination_);
    }

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

        emit PendingLenderSet(_pendingLender = pendingLender_);
    }

    function impairLoan() external override {
        uint256 originalNextPaymentDueDate_ = _nextPaymentDueDate;

        require(msg.sender == _lender, "ML:IL:NOT_LENDER");

        // If the loan is late, do not change the payment due date.
        uint256 newPaymentDueDate_ = block.timestamp > originalNextPaymentDueDate_ ? originalNextPaymentDueDate_ : block.timestamp;

        emit LoanImpaired(newPaymentDueDate_);

        _nextPaymentDueDate         = newPaymentDueDate_;
        _originalNextPaymentDueDate = originalNextPaymentDueDate_;  // Store the existing payment due date to enable reversion.
    }

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

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

        require(
            _refinanceCommitment == (refinanceCommitment_ = _getRefinanceCommitment(refinancer_, deadline_, calls_)),
            "ML:RNT:COMMITMENT_MISMATCH"
        );

        _refinanceCommitment = bytes32(0);

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

    function skim(address token_, address destination_) external override whenProtocolNotPaused returns (uint256 skimmed_) {
        emit Skimmed(token_, skimmed_ = getUnaccountedAmount(token_), destination_);
        require(ERC20Helper.transfer(token_, destination_, skimmed_), "ML: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 getClosingPaymentBreakdown() public view override returns (uint256 principal_, uint256 interest_, uint256 fees_) {
        (
          uint256 delegateServiceFee_,
          uint256 delegateRefinanceFee_,
          uint256 platformServiceFee_,
          uint256 platformRefinanceFee_
        ) = IMapleLoanFeeManager(_feeManager).getServiceFeeBreakdown(address(this), _paymentsRemaining);

        fees_ = delegateServiceFee_ + platformServiceFee_ + delegateRefinanceFee_ + platformRefinanceFee_;

        // Compute interest and include any uncaptured interest from refinance.
        interest_ = (((principal_ = _principal) * _closingRate) / SCALED_ONE) + _refinanceInterest;
    }

    function getNextPaymentDetailedBreakdown()
        public view override returns (
            uint256 principal_,
            uint256[3] memory interest_,
            uint256[2] memory fees_
        )
    {
        ( principal_, interest_, fees_ )
            = _getPaymentBreakdown(
                block.timestamp,
                _nextPaymentDueDate,
                _paymentInterval,
                _principal,
                _endingPrincipal,
                _paymentsRemaining,
                _interestRate,
                _lateFeeRate,
                _lateInterestPremium
            );
    }

    function getNextPaymentBreakdown() public view override returns (uint256 principal_, uint256 interest_, uint256 fees_) {
        uint256[3] memory interestArray_;
        uint256[2] memory feesArray_;

        ( principal_, interestArray_, feesArray_ ) = _getPaymentBreakdown(
            block.timestamp,
            _nextPaymentDueDate,
            _paymentInterval,
            _principal,
            _endingPrincipal,
            _paymentsRemaining,
            _interestRate,
            _lateFeeRate,
            _lateInterestPremium
        );

        interest_ = interestArray_[0] + interestArray_[1] + interestArray_[2];
        fees_     = feesArray_[0]     + feesArray_[1];
    }

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

    function getUnaccountedAmount(address asset_) public view override returns (uint256 unaccountedAmount_) {
        return IERC20(asset_).balanceOf(address(this))
            - (asset_ == _collateralAsset ? _collateral    : uint256(0))   // `_collateral` is `_collateralAsset` accounted for.
            - (asset_ == _fundsAsset      ? _drawableFunds : uint256(0));  // `_drawableFunds` is `_fundsAsset` accounted for.
    }

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

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

    function closingRate() external view override returns (uint256 closingRate_) {
        return _closingRate;
    }

    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 drawableFunds() external view override returns (uint256 drawableFunds_) {
        return _drawableFunds;
    }

    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 feeManager() external view override returns (address feeManager_) {
        return _feeManager;
    }

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

    function globals() public view override returns (address globals_) {
        globals_ = IMapleProxyFactoryLike(_factory()).mapleGlobals();
    }

    function governor() public view override returns (address governor_) {
        governor_ = IMapleGlobalsLike(globals()).governor();
    }

    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 originalNextPaymentDueDate() external view override returns (uint256 originalNextPaymentDueDate_) {
        return _originalNextPaymentDueDate;
    }

    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 principal() external view override returns (uint256 principal_) {
        return _principal;
    }

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

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

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

    function isImpaired() public view override returns (bool isImpaired_) {
        return _originalNextPaymentDueDate != uint256(0);
    }

    /******************************************************************************************************************************/
    /*** 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);
        _closingRate         = uint256(0);
        _lateFeeRate         = uint256(0);
        _lateInterestPremium = uint256(0);

        _endingPrincipal = uint256(0);

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

        _originalNextPaymentDueDate = uint256(0);
    }

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

    /******************************************************************************************************************************/
    /*** 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_ - 1) / 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 view
        returns (
            uint256 principalAmount_,
            uint256[3] memory interest_,
            uint256[2] memory fees_
        )
    {
        ( principalAmount_, interest_[0] ) = _getInstallment(
            principal_,
            endingPrincipal_,
            interestRate_,
            paymentInterval_,
            paymentsRemaining_
        );

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

        interest_[1] = _getLateInterest(
            currentTime_,
            principal_,
            interestRate_,
            nextPaymentDueDate_,
            lateFeeRate_,
            lateInterestPremium_
        );

        interest_[2] = _refinanceInterest;

        (
            uint256 delegateServiceFee_,
            uint256 delegateRefinanceFee_,
            uint256 platformServiceFee_,
            uint256 platformRefinanceFee_
        ) = IMapleLoanFeeManager(_feeManager).getServiceFeeBreakdown(address(this), 1);

        fees_[0] = delegateServiceFee_ + delegateRefinanceFee_;
        fees_[1] = platformServiceFee_ + platformRefinanceFee_;
    }

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

        uint256 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 + (86400n - 1n)) / 86400n) * 86400n = 86400n
        // ((86401n - 0n + (86400n - 1n)) / 86400n) * 86400n = 172800n
        uint256 fullDaysLate = ((currentTime_ - nextPaymentDueDate_ + (1 days - 1)) / 1 days) * 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_));
    }

    function _handleServiceFeePayment(uint256 numberOfPayments_) internal returns (uint256 fees_) {
        uint256 balanceBeforeServiceFees_ = IERC20(_fundsAsset).balanceOf(address(this));

        IMapleLoanFeeManager(_feeManager).payServiceFees(_fundsAsset, numberOfPayments_);

        uint256 balanceAfterServiceFees_ = IERC20(_fundsAsset).balanceOf(address(this));

        if (balanceBeforeServiceFees_ > balanceAfterServiceFees_) {
            _drawableFunds -= (fees_ = balanceBeforeServiceFees_ - balanceAfterServiceFees_);
        } else {
            _drawableFunds += balanceAfterServiceFees_ - balanceBeforeServiceFees_;
        }
    }

    function _handleImpairment() internal {
        if (!isImpaired()) return;
        _originalNextPaymentDueDate = uint256(0);
    }

    /**
     *  @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 2 of 16 : MapleLoanStorage.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;

/// @title MapleLoanStorage defines the storage layout of MapleLoan.
abstract contract MapleLoanStorage {

    // 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 _closingRate;          // The fee rate (applied to principal) to close the loan.
    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 __deprecated_claimableFunds;  // Deprecated storage slot for `claimableFunds`.
    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;  // Keccak-256 hash of the parameters of proposed terms of a refinance: `refinancer_`, `deadline_`, and `calls_`.

    uint256 internal _refinanceInterest;  // Amount of accrued interest between the last payment on a loan and the time the refinance takes effect.

    // Establishment fees
    uint256 internal __deprecated_delegateFee;  // Deprecated storage slot for `delegateFee`.
    uint256 internal __deprecated_treasuryFee;  // Deprecated storage slot for `treasuryFee`.

    // Pool V2 dependencies
    address internal _feeManager;  // Address responsible for calculating and handling fees

    // Triggered defaults
    uint256 internal _originalNextPaymentDueDate;  // Stores the original `nextPaymentDueDate` in order to allow triggered defaults to be reverted.
}

File 3 of 16 : IMapleLoan.sol
// SPDX-License-Identifier: BUSL-1.1
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 fee rate (applied to principal) to close the loan.
     *       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 closingRate() external view returns (uint256 closingRate_);

    /**
     *  @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 amount of funds that have yet to be drawn down by the borrower.
     */
    function drawableFunds() external view returns (uint256 drawableFunds_);

    /**
     *  @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 address of the contract that handles payments of fees on behalf of the loan.
     */
    function feeManager() external view returns (address feeManager_);

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

    /**
     *  @dev The Maple globals address
     */
    function globals() external view returns (address globals_);

    /**
     *  @dev The address of the Maple Governor.
     */
    function governor() external view returns (address governor_);

    /**
     *  @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 saved original payment due date from a loan impairment.
     */
    function originalNextPaymentDueDate() external view returns (uint256 originalNextPaymentDueDate_);

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

    /******************************************************************************************************************************/
    /*** 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.
     *  @return refinanceCommitment_ The hash of the accepted refinance agreement.
     */
    function acceptNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external returns (bytes32 refinanceCommitment_);

    /**
     *  @dev    Repay all principal and interest and close a loan.
     *          FUNDS SHOULD NOT BE TRANSFERRED TO THIS CONTRACT NON-ATOMICALLY. IF THEY ARE, THE BALANCE MAY BE STOLEN USING `skim`.
     *  @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 fees_      The portion of the amount paying service fees.
     */
    function closeLoan(uint256 amount_) external returns (uint256 principal_, uint256 interest_, uint256 fees_);

    /**
     *  @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.
     *  @return fundsLent_ The amount funded.
     */
    function fundLoan(address lender_) external returns (uint256 fundsLent_);

    /**
     *  @dev    Make a payment to the loan.
     *          FUNDS SHOULD NOT BE TRANSFERRED TO THIS CONTRACT NON-ATOMICALLY. IF THEY ARE, THE BALANCE MAY BE STOLEN USING `skim`.
     *  @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 fees_      The portion of the amount paying service fees.
     */
    function makePayment(uint256 amount_) external returns (uint256 principal_, uint256 interest_, uint256 fees_);

    /**
     *  @dev    Post collateral to the loan.
     *          FUNDS SHOULD NOT BE TRANSFERRED TO THIS CONTRACT NON-ATOMICALLY. IF THEY ARE, THE BALANCE MAY BE STOLEN USING `skim`.
     *  @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.
     *  @return refinanceCommitment_ The hash of the proposed refinance agreement.
     */
    function proposeNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external returns (bytes32 refinanceCommitment_);

    /**
     *  @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.
     *  @return refinanceCommitment_ The hash of the rejected refinance agreement.
     */
    function rejectNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external returns (bytes32 refinanceCommitment_);

    /**
     *  @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 Remove the loan impairment by restoring the original payment due date.
     */
    function removeLoanImpairment() external;

    /**
     *  @dev    Return funds to the loan (opposite of drawing down).
     *          FUNDS SHOULD NOT BE TRANSFERRED TO THIS CONTRACT NON-ATOMICALLY. IF THEY ARE, THE BALANCE MAY BE STOLEN USING `skim`.
     *  @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 all token that is not accounted for by the loan (i.e. not `collateral` or `drawableFunds`).
     *  @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_);

    /**
     *  @dev   Fast forward the next payment due date to the current time.
     *         This enables the pool delegate to force a payment (or default).
     */
    function impairLoan() external;

    /******************************************************************************************************************************/
    /*** 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 to close the loan.
     *  @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 fees_      The portion of the total amount that will go towards fees.
     */
    function getClosingPaymentBreakdown() external view returns (uint256 principal_, uint256 interest_, uint256 fees_);

    /**
     *  @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 fees_      The portion of the total amount that will go towards paying administrative fees.
     */
    function getNextPaymentBreakdown() external view returns (uint256 principal_, uint256 interest_, uint256 fees_);

    /**
     *  @dev    Get the detailed 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.
     *                      [0] Interest from the payment interval.
     *                      [1] Late interest.
     *                      [2] Refinance interest.
     *  @return fees_      The portion of the total amount that will go towards paying administrative fees.
     *                      [0] Delegate fees.
     *                      [1] Platform fees.
     */
    function getNextPaymentDetailedBreakdown() external view returns (uint256 principal_, uint256[3] memory interest_, uint256[2] memory fees_);

    /**
     *  @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    Get the amount on an asset that in not accounted for by the accounting variables (and thus can be skimmed).
     *  @param  asset_             The address of a asset contract.
     *  @return unaccountedAmount_ The amount that is not accounted for.
     */
    function getUnaccountedAmount(address asset_) external view returns (uint256 unaccountedAmount_);

    /**
     *  @dev    Return if the loan has been impaired.
     *  @return isImpaired_ Is the loan impaired or not.
     */
     function isImpaired() external view returns (bool isImpaired_);

}

File 4 of 16 : IMapleLoanEvents.sol
// SPDX-License-Identifier: BUSL-1.1
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   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 returned.
     *  @param amount_ The amount of funds returned.
     */
    event FundsReturned(uint256 amount_);

    /**
     *  @dev   Loan was initialized.
     *  @param borrower_    The address of the borrower.
     *  @param feeManager_  The address of the entity responsible for calculating fees
     *  @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]: closingFeeRate,
     *                       [2]: lateFeeRate,
     *                       [3]: lateInterestPremium
     *  @param fees_        Array of fees:
     *                       [0]: delegateOriginationFee,
     *                       [1]: delegateServiceFee
     */
    event Initialized(
        address indexed borrower_,
        address indexed feeManager_,
        address[2] assets_,
        uint256[3] termDetails_,
        uint256[3] amounts_,
        uint256[4] rates_,
        uint256[2] fees_
    );

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

    /**
     *  @dev   The next payment due date was fast forwarded to the current time, activating the grace period.
     *         This is emitted when the pool delegate wants to force a payment (or default).
     *  @param nextPaymentDueDate_ The new next payment due date.
     */
    event LoanImpaired(uint256 nextPaymentDueDate_);

    /**
     *  @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 feesPaid_      The portion of the total amount that went towards fees.
     */
    event LoanClosed(uint256 principalPaid_, uint256 interestPaid_, uint256 feesPaid_);

    /**
     *  @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   The next payment due date was restored to it's original value, reverting the action of loan impairment.
     *  @param nextPaymentDueDate_ The new next payment due date.
     */
    event NextPaymentDueDateRestored(uint256 nextPaymentDueDate_);

    /**
     *  @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 fees_          The portion of the total amount that went towards fees.
     */
    event PaymentMade(uint256 principalPaid_, uint256 interestPaid_, uint256 fees_);

    /**
     *  @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 : IMapleLoanFeeManager.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;

interface IMapleLoanFeeManager {

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

    /**
     *  @dev   New fee terms have been set.
     *  @param loan_                   The address of the loan contract.
     *  @param delegateOriginationFee_ The new value for delegate origination fee.
     *  @param delegateServiceFee_     The new value for delegate service fee.
     */
    event FeeTermsUpdated(address loan_, uint256 delegateOriginationFee_, uint256 delegateServiceFee_);

    /**
     *  @dev   A fee payment was made.
     *  @param loan_                   The address of the loan contract.
     *  @param delegateOriginationFee_ The amount of delegate origination fee paid.
     *  @param platformOriginationFee_ The amount of platform origination fee paid.
    */
    event OriginationFeesPaid(address loan_, uint256 delegateOriginationFee_, uint256 platformOriginationFee_);

    /**
     *  @dev   New fee terms have been set.
     *  @param loan_               The address of the loan contract.
     *  @param platformServiceFee_ The new value for the platform service fee.
     */
    event PlatformServiceFeeUpdated(address loan_, uint256 platformServiceFee_);

    /**
     *  @dev   New fee terms have been set.
     *  @param loan_                      The address of the loan contract.
     *  @param partialPlatformServiceFee_ The  value for the platform service fee.
     *  @param partialDelegateServiceFee_ The  value for the delegate service fee.
     */
    event PartialRefinanceServiceFeesUpdated(address loan_, uint256 partialPlatformServiceFee_, uint256 partialDelegateServiceFee_);

    /**
     *  @dev   A fee payment was made.
     *  @param loan_                               The address of the loan contract.
     *  @param delegateServiceFee_                 The amount of delegate service fee paid.
     *  @param partialRefinanceDelegateServiceFee_ The amount of partial delegate service fee from refinance paid.
     *  @param platformServiceFee_                 The amount of platform service fee paid.
     *  @param partialRefinancePlatformServiceFee_ The amount of partial platform service fee from refinance paid.
    */
    event ServiceFeesPaid(address loan_, uint256 delegateServiceFee_, uint256 partialRefinanceDelegateServiceFee_, uint256 platformServiceFee_, uint256 partialRefinancePlatformServiceFee_);

    /******************************************************************************************************************************/
    /*** Payment Functions                                                                                                      ***/
    /******************************************************************************************************************************/

    /**
     *  @dev   Called during `makePayment`, performs fee payments to the pool delegate and treasury.
     *  @param asset_            The address asset in which fees were paid.
     *  @param numberOfPayments_ The number of payments for which service fees will be paid.
     */
    function payServiceFees(address asset_, uint256 numberOfPayments_) external returns (uint256 feePaid_);

    /**
     *  @dev    Called during `fundLoan`, performs fee payments to poolDelegate and treasury.
     *  @param  asset_              The address asset in which fees were paid.
     *  @param  principalRequested_ The total amount of principal requested, which will be used to calculate fees.
     *  @return feePaid_            The total amount of fees paid.
     */
    function payOriginationFees(address asset_, uint256 principalRequested_) external returns (uint256 feePaid_);

    /******************************************************************************************************************************/
    /*** Fee Update Functions                                                                                                   ***/
    /******************************************************************************************************************************/

    /**
     *  @dev   Called during loan creation or refinance, sets the fee terms.
     *  @param delegateOriginationFee_ The amount of delegate origination fee to be paid.
     *  @param delegateServiceFee_     The amount of delegate service fee to be paid.
     */
    function updateDelegateFeeTerms(uint256 delegateOriginationFee_, uint256 delegateServiceFee_) external;

    /**
     *  @dev   Called during loan refinance to save the partial service fees accrued.
     *  @param principalRequested_   The amount of principal pre-refinance requested.
     *  @param timeSinceLastDueDate_ The amount of time since last payment due date.
     */
    function updateRefinanceServiceFees(uint256 principalRequested_, uint256 timeSinceLastDueDate_) external;

    /**
     *  @dev Function called by loans to update the saved platform service fee rate.
     */
    function updatePlatformServiceFee(uint256 principalRequested_, uint256 paymentInterval_) external;

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

    /**
     *  @dev    Gets the delegate origination fee for the given loan.
     *  @param  loan_                   The address of the loan contract.
     *  @return delegateOriginationFee_ The amount of origination to be paid to delegate.
     */
    function delegateOriginationFee(address loan_) external view returns (uint256 delegateOriginationFee_);

    /**
     *  @dev    Gets the delegate service fee rate for the given loan.
     *  @param  loan_               The address of the loan contract.
     *  @return delegateServiceFee_ The amount of delegate service fee to be paid.
     */
    function delegateServiceFee(address loan_) external view returns (uint256 delegateServiceFee_);

    /**
     *  @dev    Gets the delegate service fee rate for the given loan.
     *  @param  loan_                        The address of the loan contract.
     *  @return delegateRefinanceServiceFee_ The amount of delegate service fee to be paid.
     */
    function delegateRefinanceServiceFee(address loan_) external view returns (uint256 delegateRefinanceServiceFee_);

    /**
     *  @dev    Gets the delegate service fee for the given loan.
     *  @param  loan_               The address of the loan contract.
     *  @param  interval_           The time, in seconds, to get the proportional fee for
     *  @return delegateServiceFee_ The amount of delegate service fee to be paid.
     */
    function getDelegateServiceFeesForPeriod(address loan_, uint256 interval_) external view returns (uint256 delegateServiceFee_);

     /**
     *  @dev    Gets the sum of all origination fees for the given loan.
     *  @param  loan_               The address of the loan contract.
     *  @param  principalRequested_ The amount of principal requested in the loan.
     *  @return originationFees_    The amount of origination fees to be paid.
     */
    function getOriginationFees(address loan_, uint256 principalRequested_) external view  returns (uint256 originationFees_);

    /**
     *  @dev    Gets the platform origination fee value for the given loan.
     *  @param  loan_                   The address of the loan contract.
     *  @param  principalRequested_     The amount of principal requested in the loan.
     *  @return platformOriginationFee_ The amount of platform origination fee to be paid.
     */
    function getPlatformOriginationFee(address loan_, uint256 principalRequested_) external view returns (uint256 platformOriginationFee_);

    /**
     *  @dev    Gets the delegate service fee for the given loan.
     *  @param  loan_               The address of the loan contract.
     *  @param  principalRequested_ The amount of principal requested in the loan.
     *  @param  interval_           The time, in seconds, to get the proportional fee for
     *  @return platformServiceFee_ The amount of platform service fee to be paid.
     */
    function getPlatformServiceFeeForPeriod(address loan_, uint256 principalRequested_, uint256 interval_) external view returns (uint256 platformServiceFee_);

    /**
     *  @dev    Gets the service fees for the given interval.
     *  @param  loan_                 The address of the loan contract.
     *  @param  numberOfPayments_     The number of payments being paid.
     *  @return delegateServiceFee_   The amount of delegate service fee to be paid.
     *  @return delegateRefinanceFee_ The amount of delegate refinance fee to be paid.
     *  @return platformServiceFee_   The amount of platform service fee to be paid.
     *  @return platformRefinanceFee_ The amount of platform refinance fee to be paid.
     */
    function getServiceFeeBreakdown(address loan_, uint256 numberOfPayments_) external view returns (
        uint256 delegateServiceFee_,
        uint256 delegateRefinanceFee_,
        uint256 platformServiceFee_,
        uint256 platformRefinanceFee_
    );

    /**
     *  @dev    Gets the service fees for the given interval.
     *  @param  loan_             The address of the loan contract.
     *  @param  numberOfPayments_ The number of payments being paid.
     *  @return serviceFees_      The amount of platform service fee to be paid.
     */
    function getServiceFees(address loan_, uint256 numberOfPayments_) external view returns (uint256 serviceFees_);

    /**
     *  @dev    Gets the service fees for the given interval.
     *  @param  loan_        The address of the loan contract.
     *  @param  interval_    The time, in seconds, to get the proportional fee for
     *  @return serviceFees_ The amount of platform service fee to be paid.
     */
    function getServiceFeesForPeriod(address loan_, uint256 interval_) external view returns (uint256 serviceFees_);

    /**
     *  @dev    Gets the global contract address.
     *  @return globals_ The address of the global contract.
     */
    function globals() external view returns (address globals_);

    /**
     *  @dev    Gets the platform fee rate for the given loan.
     *  @param  loan_                        The address of the loan contract.
     *  @return platformRefinanceServiceFee_ The amount of platform service fee to be paid.
     */
    function platformRefinanceServiceFee(address loan_) external view returns (uint256 platformRefinanceServiceFee_);

    /**
     *  @dev    Gets the platform fee rate for the given loan.
     *  @param  loan_              The address of the loan contract.
     *  @return platformServiceFee The amount of platform service fee to be paid.
     */
    function platformServiceFee(address loan_) external view returns (uint256 platformServiceFee);

}

File 6 of 16 : Interfaces.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;

interface IMapleGlobalsLike {

    function governor() external view returns (address governor_);

    function isBorrower(address account_) external view returns (bool isBorrower_);

    function isCollateralAsset(address collateralAsset_) external view returns (bool isCollateralAsset_);

    function isFactory(bytes32 factoryId_, address factory_) external view returns (bool isValid_);

    function isPoolAsset(address poolAsset_) external view returns (bool isValid_);

    function mapleTreasury() external view returns (address governor_);

    function platformOriginationFeeRate(address pool_) external view returns (uint256 platformOriginationFeeRate_);

    function platformServiceFeeRate(address pool_) external view returns (uint256 platformFee_);

    function protocolPaused() external view returns (bool protocolPaused_);

    function securityAdmin() external view returns (address securityAdmin_);

}

interface ILenderLike {

    function claim(uint256 principal_, uint256 interest_, uint256 previousPaymentDueDate_, uint256 nextPaymentDueDate_) external;

    function factory() external view returns (address factory_);

}

interface ILoanLike {

    function factory() external view returns (address factory_);

    function fundsAsset() external view returns (address asset_);

    function lender() external view returns (address lender_);

    function paymentInterval() external view returns (uint256 paymentInterval_);

    function paymentsRemaining() external view returns (uint256 paymentsRemaining_);

    function principal() external view returns (uint256 principal_);

    function principalRequested() external view returns (uint256 principalRequested_);

}

interface ILoanManagerLike {

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

    function poolManager() external view returns (address poolManager_);

}

interface IMapleFeeManagerLike {

    function updateDelegateFeeTerms(uint256 delegateOriginationFee_, uint256 delegateServiceFee_) external;

    function updatePlatformServiceFee(uint256 principalRequested_, uint256 paymentInterval_) external;

}

interface IMapleProxyFactoryLike {

    function isInstance(address instance_) external view returns (bool isInstance_);

    function mapleGlobals() external view returns (address mapleGlobals_);

}

interface IPoolManagerLike {

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

}

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/modules/proxy-factory/modules/ds-test/src/"
  ],
  "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":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"}],"name":"FundsReturned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower_","type":"address"},{"indexed":true,"internalType":"address","name":"feeManager_","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]"},{"indexed":false,"internalType":"uint256[2]","name":"fees_","type":"uint256[2]"}],"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":"feesPaid_","type":"uint256"}],"name":"LoanClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"nextPaymentDueDate_","type":"uint256"}],"name":"LoanImpaired","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":"nextPaymentDueDate_","type":"uint256"}],"name":"NextPaymentDueDateRestored","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":"fees_","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[]"}],"name":"acceptNewTerms","outputs":[{"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"}],"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"}],"name":"closeLoan","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"interest_","type":"uint256"},{"internalType":"uint256","name":"fees_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"closingRate","outputs":[{"internalType":"uint256","name":"closingRate_","type":"uint256"}],"stateMutability":"view","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":"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":"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":[],"name":"feeManager","outputs":[{"internalType":"address","name":"feeManager_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lender_","type":"address"}],"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":"getClosingPaymentBreakdown","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"interest_","type":"uint256"},{"internalType":"uint256","name":"fees_","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":"fees_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNextPaymentDetailedBreakdown","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256[3]","name":"interest_","type":"uint256[3]"},{"internalType":"uint256[2]","name":"fees_","type":"uint256[2]"}],"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":[{"internalType":"address","name":"asset_","type":"address"}],"name":"getUnaccountedAmount","outputs":[{"internalType":"uint256","name":"unaccountedAmount_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globals","outputs":[{"internalType":"address","name":"globals_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governor","outputs":[{"internalType":"address","name":"governor_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gracePeriod","outputs":[{"internalType":"uint256","name":"gracePeriod_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"impairLoan","outputs":[],"stateMutability":"nonpayable","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":"isImpaired","outputs":[{"internalType":"bool","name":"isImpaired_","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":"fees_","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":"originalNextPaymentDueDate","outputs":[{"internalType":"uint256","name":"originalNextPaymentDueDate_","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":[{"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"}],"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":[{"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"}],"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":[],"name":"removeLoanImpairment","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":[{"internalType":"uint256","name":"toVersion_","type":"uint256"},{"internalType":"bytes","name":"arguments_","type":"bytes"}],"name":"upgrade","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50614822806100206000396000f3fe608060405234801561001057600080fd5b50600436106103835760003560e01c80637c3a00fd116101de578063ba5d30781161010f578063d05951a0116100ad578063d4754d5f1161007c578063d4754d5f146106a8578063d784d426146106b0578063d8dfeb45146106c3578063e48f6faf146106cb57600080fd5b8063d05951a01461065a578063d0fb02031461066d578063d3bcd5e21461067e578063d41ddc961461069557600080fd5b8063c3124525116100e9578063c312452514610624578063c3fbb6fd1461062c578063c45a01551461063f578063ccc044841461064757600080fd5b8063ba5d307814610603578063ba83276b1461060b578063bcead63e1461061357600080fd5b8063a29d7aab1161017c578063acb522b411610156578063acb522b4146105d8578063b86a513e146105eb578063b96b5c99146105f3578063b9b1f4e3146105fb57600080fd5b8063a29d7aab146105ac578063a97d1161146105bf578063aabaecd6146105c757600080fd5b80638ffc9215116101b85780638ffc92151461058157806396663914146105895780639e10320b14610591578063a06db7dc146105a457600080fd5b80637c3a00fd146105555780637df1f1b91461055d57806387accaf11461056e57600080fd5b806345755dd6116102b85780636174b27211610256578063712b772f11610230578063712b772f1461052a57806375a206761461053d57806377b3c55c146105455780637a0e6fa11461054d57600080fd5b80636174b272146104f3578063700f50061461050657806370a10c891461051757600080fd5b806350f2012f1161029257806350f2012f146104995780635114cb52146104ac5780635c60da1b146104da5780635eeb53b4146104e257600080fd5b806345755dd61461044b57806347350e9f1461045e5780634eac42351461048657600080fd5b8063227000371161032557806339ba9f86116102ff57806339ba9f861461041757806339d65834146104285780633b99bcee146104305780634003f34d1461044357600080fd5b806322700037146103f4578063267f4ac3146103fc5780632ead10981461040f57600080fd5b80630fe3d9b7116103615780630fe3d9b7146103c9578063144ee4b5146103d15780631cc1cf46146103d95780631f3f19ab146103e157600080fd5b806301daa38f146103885780630895326f146103925780630c340a24146103a9575b600080fd5b6103906106de565b005b6013545b6040519081526020015b60405180910390f35b6103b161081d565b6040516001600160a01b0390911681526020016103a0565b61039061089c565b6103906109d4565b600754610396565b6103906103ef366004614266565b610aec565b601a54610396565b61039061040a366004614266565b610cee565b600b54610396565b6005546001600160a01b03166103b1565b600954610396565b61039061043e366004614433565b610d8a565b601254610396565b6103966104593660046143dc565b610fa0565b61047161046c366004614266565b61110a565b604080519283526020830191909152016103a0565b6103966104943660046143dc565b6112fb565b6103966104a73660046143dc565b611343565b6104bf6104ba3660046143dc565b6114a4565b604080519384526020840192909252908201526060016103a0565b6103b1611ae4565b6003546001600160a01b03166103b1565b610396610501366004614266565b611aee565b6002546001600160a01b03166103b1565b61039661052536600461432e565b611bc2565b6103966105383660046142a0565b6120d6565b600c54610396565b600a54610396565b61039661220d565b600854610396565b6000546001600160a01b03166103b1565b61039661057c36600461432e565b612249565b600d54610396565b6104bf6123e6565b61039661059f3660046143dc565b6124e3565b600654610396565b601a5460405190151581526020016103a0565b601654610396565b6004546001600160a01b03166103b1565b6103966105e636600461432e565b612506565b600e54610396565b6104bf612699565b600f54610396565b601454610396565b601554610396565b6001546001600160a01b03166103b1565b6103b1612715565b61039061063a3660046142d9565b612757565b6103b16127fc565b61039661065536600461440e565b612806565b6104bf6106683660046143dc565b612a48565b6019546001600160a01b03166103b1565b61068661306b565b6040516103a093929190614659565b6103906106a336600461440e565b6130aa565b61039061329b565b6103906106be366004614266565b61333f565b601154610396565b6103966106d9366004614266565b6133cb565b6106e6612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561071e57600080fd5b505afa158015610732573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061075691906143ba565b1561077c5760405162461bcd60e51b81526004016107739061462e565b60405180910390fd5b6002546001600160a01b031633146107d65760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a41423a4e4f545f50454e44494e475f424f52524f5745520000000000006044820152606401610773565b600280546001600160a01b031990811690915560008054339216821781556040517f29bac0ac2b15405bfcc160bb74b6ae7a559b7674ce33db80785ada73e38204d29190a2565b6000610827612715565b6001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b15801561085f57600080fd5b505afa158015610873573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108979190614283565b905090565b6108a4612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b1580156108dc57600080fd5b505afa1580156108f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061091491906143ba565b156109315760405162461bcd60e51b81526004016107739061462e565b6003546001600160a01b0316331461098b5760405162461bcd60e51b815260206004820152601860248201527f4d4c3a414c3a4e4f545f50454e44494e475f4c454e44455200000000000000006044820152606401610773565b600380546001600160a01b031990811690915560018054339216821790556040517fd6165838d2e3db87aa1002b548048673fc6427eefbd1b914e100f3a0deae23e390600090a2565b601a546001546001600160a01b03163314610a255760405162461bcd60e51b815260206004820152601160248201527026a61d2926249d2727aa2fa622a72222a960791b6044820152606401610773565b80610a685760405162461bcd60e51b815260206004820152601360248201527213530e9493124e9393d517d253541052549151606a1b6044820152606401610773565b80421115610aab5760405162461bcd60e51b815260206004820152601060248201526f4d4c3a524c493a504153545f4441544560801b6044820152606401610773565b60128190556000601a556040518181527f962d5a38dc48af765ecb4f177a88713b592b5bf59e64f497ab33af13b7c0e9de906020015b60405180910390a150565b610af4612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015610b2c57600080fd5b505afa158015610b40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6491906143ba565b15610b815760405162461bcd60e51b81526004016107739061462e565b6000546001600160a01b03163314610bd15760405162461bcd60e51b815260206004820152601360248201527226a61d29a8211d2727aa2fa127a92927aba2a960691b6044820152606401610773565b610bd9612715565b60405163eaf6e48360e01b81526001600160a01b038381166004830152919091169063eaf6e4839060240160206040518083038186803b158015610c1c57600080fd5b505afa158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5491906143ba565b610ca05760405162461bcd60e51b815260206004820152601760248201527f4d4c3a5350423a494e56414c49445f424f52524f5745520000000000000000006044820152606401610773565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f10f06072822ef73860fedb88933f968d20bb4aadce8a8d360d1124cb6ce1e0b290602001610ae1565b6001546001600160a01b03163314610d3c5760405162461bcd60e51b815260206004820152601160248201527026a61d29a8261d2727aa2fa622a72222a960791b6044820152606401610773565b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527fa3ab02442c80a4102475683f16513c9139a89142be9db9804edfcfbb379fc49290602001610ae1565b610d92612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015610dca57600080fd5b505afa158015610dde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0291906143ba565b15610e1f5760405162461bcd60e51b81526004016107739061462e565b610e27612715565b6001600160a01b031663be7c13f76040518163ffffffff1660e01b815260040160206040518083038186803b158015610e5f57600080fd5b505afa158015610e73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e979190614283565b6001600160a01b0316336001600160a01b031614610ef75760405162461bcd60e51b815260206004820152601760248201527f4d4c3a553a4e4f545f53454355524954595f41444d494e0000000000000000006044820152606401610773565b7faaaa7ee6b0c2f4ee1fa7312c7d5b3623a434da5a1a9ce3cb6e629caa23454ab6838383604051610f2a939291906146ba565b60405180910390a1610f3a6138ac565b6001600160a01b031663fe69f7088484846040518463ffffffff1660e01b8152600401610f69939291906146ba565b600060405180830381600087803b158015610f8357600080fd5b505af1158015610f97573d6000803e3d6000fd5b50505050505050565b6000610faa612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015610fe257600080fd5b505afa158015610ff6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101a91906143ba565b156110375760405162461bcd60e51b81526004016107739061462e565b8115806110575750600554611057906001600160a01b03163330856138db565b6110a35760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a52463a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b6005546110b8906001600160a01b0316611aee565b905080600f60008282546110cc919061471b565b90915550506040518181527f278e51d3323fbf18b9fb8df3f8b97e31b145bc1146c52e764cf4aa1bfc4ba17d906020015b60405180910390a1919050565b60015460009081906001600160a01b0316331461115b5760405162461bcd60e51b815260206004820152600f60248201526e26a61d291d2727aa2fa622a72222a960891b6044820152606401610773565b60125480158015906111785750600654611175908261471b565b42115b6111ba5760405162461bcd60e51b815260206004820152601360248201527213530e948e9393d517d25397d1115190555315606a1b6044820152606401610773565b6111c2613952565b60006011819055600f8190556004546001600160a01b0316906111e482611aee565b94508414806111f957506111f981868661398b565b61123e5760405162461bcd60e51b815260206004820152601660248201527513530e948e90d7d514905394d1915497d1905253115160521b6044820152606401610773565b6005546001600160a01b0316600061125582611aee565b945084148061126a575061126a81878661398b565b6112af5760405162461bcd60e51b815260206004820152601660248201527513530e948e9197d514905394d1915497d1905253115160521b6044820152606401610773565b60408051868152602081018690526001600160a01b038816917f027e623aab0b174da270ff529cad1c54f09182651e07437d2ac557929b9e5b49910160405180910390a2505050915091565b60008061131d60145484600f546113129190614774565b600d54600c546139c8565b60115490915080821161133157600061133b565b61133b8183614774565b949350505050565b600061134d612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561138557600080fd5b505afa158015611399573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113bd91906143ba565b156113da5760405162461bcd60e51b81526004016107739061462e565b8115806113fa57506004546113fa906001600160a01b03163330856138db565b6114465760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a50433a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b60045461145b906001600160a01b0316611aee565b9050806011600082825461146f919061471b565b90915550506040518181527f437d44b2c697fb69e2b2f25f57fd844e376c25ed28ed5a9c4be88aa1e5c87d12906020016110fd565b60008054819081906001600160a01b03163314156117a7576114c4612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b1580156114fc57600080fd5b505afa158015611510573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061153491906143ba565b156115515760405162461bcd60e51b81526004016107739061462e565b8315806115715750600554611571906001600160a01b03163330876138db565b6115bd5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a4d503a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b6115c5613a18565b6115cd612699565b506000601681905591945092506115e4838561471b565b60055490915081906115fe906001600160a01b0316611aee565b600f5461160b919061471b565b6116159190614774565b600f556116226001613a28565b60135460125491935090600060018314156116445761163f613952565b611681565b600754611651908361471b565b905080601281905550866014600082825461166c9190614774565b9091555061167d9050600184614774565b6013555b60408051888152602081018890529081018690527fcf358e925a8e033c6db877f18d10df6f21cd04ef165537bad5fc814eb23af9609060600160405180910390a16005546001546116df916001600160a01b0390811691168661398b565b6116fb5760405162461bcd60e51b8152600401610773906145ff565b60015460405163b5add71760e01b8152600481018990526024810188905260448101849052606481018390526001600160a01b039091169063b5add71790608401600060405180830381600087803b15801561175657600080fd5b505af115801561176a573d6000803e3d6000fd5b50506001546040518781526001600160a01b0390911692506000805160206147cd833981519152915060200160405180910390a250505050611add565b600f546117b2612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b1580156117ea57600080fd5b505afa1580156117fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182291906143ba565b1561183f5760405162461bcd60e51b81526004016107739061462e565b84158061185f575060055461185f906001600160a01b03163330886138db565b6118ab5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a4d503a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b6118b3613a18565b6118bb612699565b506000601681905591955093506118d2848661471b565b60055490915081906118ec906001600160a01b0316611aee565b600f546118f9919061471b565b6119039190614774565b600f556119106001613a28565b60135460125491945090600060018314156119325761192d613952565b61196f565b60075461193f908361471b565b905080601281905550876014600082825461195a9190614774565b9091555061196b9050600184614774565b6013555b60408051898152602081018990529081018790527fcf358e925a8e033c6db877f18d10df6f21cd04ef165537bad5fc814eb23af9609060600160405180910390a16005546001546119cd916001600160a01b0390811691168661398b565b6119e95760405162461bcd60e51b8152600401610773906145ff565b60015460405163b5add71760e01b8152600481018a90526024810189905260448101849052606481018390526001600160a01b039091169063b5add71790608401600060405180830381600087803b158015611a4457600080fd5b505af1158015611a58573d6000803e3d6000fd5b50506001546040518781526001600160a01b0390911692506000805160206147cd833981519152915060200160405180910390a25050505080600f541015611adb5760405162461bcd60e51b81526020600482015260166024820152754d4c3a43414e4e4f545f5553455f4452415741424c4560501b6044820152606401610773565b505b9193909250565b6000610897613c0a565b6005546000906001600160a01b03838116911614611b0d576000611b11565b600f545b6004546001600160a01b03848116911614611b2d576000611b31565b6011545b6040516370a0823160e01b81523060048201526001600160a01b038516906370a082319060240160206040518083038186803b158015611b7057600080fd5b505afa158015611b84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba891906143f5565b611bb29190614774565b611bbc9190614774565b92915050565b6001546000906001600160a01b03163314611c135760405162461bcd60e51b815260206004820152601160248201527026a61d20a72a1d2727aa2fa622a72222a960791b6044820152606401610773565b611c1f85858585613c34565b90508060155414611c725760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a414e543a434f4d4d49544d454e545f4d49534d415443480000000000006044820152606401610773565b6001600160a01b0385163b611cc95760405162461bcd60e51b815260206004820152601960248201527f4d4c3a414e543a494e56414c49445f524546494e414e434552000000000000006044820152606401610773565b83421115611d195760405162461bcd60e51b815260206004820152601960248201527f4d4c3a414e543a455850495245445f434f4d4d49544d454e54000000000000006044820152606401610773565b600754601254600d54600082611d2f854261471b565b10611d4d57611d3e8484614774565b611d489042614774565b611d50565b60005b6019546040516341a703bb60e01b815260048101859052602481018390529192506001600160a01b03169081906341a703bb90604401600060405180830381600087803b158015611da057600080fd5b505af1158015611db4573d6000803e3d6000fd5b505050506000611dc3426124e3565b90508060166000828254611dd7919061471b565b9091555050600060158190555b88811015611eb45760008c6001600160a01b03168b8b84818110611e0a57611e0a6147a1565b9050602002810190611e1c91906146d4565b604051611e2a929190614553565b600060405180830381855af49150503d8060008114611e65576040519150601f19603f3d011682016040523d82523d6000602084013e611e6a565b606091505b5050905080611eab5760405162461bcd60e51b815260206004820152600d60248201526c13530e9053950e919052531151609a1b6044820152606401610773565b50600101611de4565b507f7150c332bd889236b6ab42cc34f0853631ceb58827f58a8697b682f13e390a8c878c8c8c8c604051611eec9594939291906145c6565b60405180910390a1600554600d5460075497506001600160a01b0390911690611f15884261471b565b601255611f20613a18565b6040516377cf0ddb60e11b815260048101829052602481018990526001600160a01b0385169063ef9e1bb690604401600060405180830381600087803b158015611f6957600080fd5b505af1158015611f7d573d6000803e3d6000fd5b50506040516327d180b560e01b81526001600160a01b03858116600483015260248201859052871692506327d180b59150604401602060405180830381600087803b158015611fcb57600080fd5b505af1158015611fdf573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061200391906143f5565b600f60008282546120149190614774565b909155506120229050613c6d565b61206e5760405162461bcd60e51b815260206004820152601e60248201527f4d4c3a414e543a494e53554646494349454e545f434f4c4c41544552414c00006044820152606401610773565b600061207983611aee565b146120c65760405162461bcd60e51b815260206004820152601760248201527f4d4c3a414e543a554e45585045435445445f46554e44530000000000000000006044820152606401610773565b5050505050505050949350505050565b60006120e0612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561211857600080fd5b505afa15801561212c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061215091906143ba565b1561216d5760405162461bcd60e51b81526004016107739061462e565b816001600160a01b0316836001600160a01b03167ff1f6a55e7ad487ac8dd8e1d4517348d3b410a7a0bc405ef87b09078dc51b23b66121ab86611aee565b60405181815290945060200160405180910390a36121ca83838361398b565b611bbc5760405162461bcd60e51b815260206004820152601460248201527313530e94ce9514905394d1915497d1905253115160621b6044820152606401610773565b600080612224601454600f54600d54600c546139c8565b601154909150818111612238576000612242565b6122428282614774565b9250505090565b6000612253612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561228b57600080fd5b505afa15801561229f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c391906143ba565b156122e05760405162461bcd60e51b81526004016107739061462e565b6000546001600160a01b031633146123305760405162461bcd60e51b815260206004820152601360248201527226a61d28272a1d2727aa2fa127a92927aba2a960691b6044820152606401610773565b428410156123805760405162461bcd60e51b815260206004820152601760248201527f4d4c3a504e543a494e56414c49445f444541444c494e450000000000000000006044820152606401610773565b7ff94d2f0322894aaf1bce14561461a8b8b6c9b11a77bbe80f20b804da8a95e4b7826123ad5760006123b9565b6123b986868686613c34565b6015819055915081868686866040516123d69594939291906145c6565b60405180910390a1949350505050565b60195460135460405163e0b58e0d60e01b815230600482015260248101919091526000918291829182918291829182916001600160a01b03169063e0b58e0d9060440160806040518083038186803b15801561244157600080fd5b505afa158015612455573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124799190614466565b93509350935093508083838661248f919061471b565b612499919061471b565b6124a3919061471b565b9450601654670de0b6b3a76400006009546014549950896124c49190614755565b6124ce9190614733565b6124d8919061471b565b955050505050909192565b6000611bbc82600754601454600e54600854601354601254600a54600b54613c8d565b6000612510612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561254857600080fd5b505afa15801561255c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061258091906143ba565b1561259d5760405162461bcd60e51b81526004016107739061462e565b6000546001600160a01b03163314806125c057506001546001600160a01b031633145b6125fd5760405162461bcd60e51b815260206004820152600e60248201526d09a9874a49ca8749c9ebe82aaa8960931b6044820152606401610773565b61260985858585613c34565b9050806015541461265c5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a524e543a434f4d4d49544d454e545f4d49534d415443480000000000006044820152606401610773565b60006015556040517f47244a449377da5fd10e98d86d118dee442e842fc34f05179c973cfcff6acba7906123d690839088908890889088906145c6565b60008060006126a66141e1565b6126ae6141ff565b6126cf42601254600754601454600e54601354600854600a54600b54613cfa565b604082015160208301518351949950929550909350916126ef919061471b565b6126f9919061471b565b6020820151825191955061270c9161471b565b92505050909192565b600061271f6138ac565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561085f57600080fd5b61275f6138ac565b6001600160a01b0316336001600160a01b0316146127b25760405162461bcd60e51b815260206004820152601060248201526f4d4c3a4d3a4e4f545f464143544f525960801b6044820152606401610773565b6127bd838383613e17565b6127f75760405162461bcd60e51b815260206004820152600b60248201526a13530e934e91905253115160aa1b6044820152606401610773565b505050565b60006108976138ac565b6000612810612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561284857600080fd5b505afa15801561285c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061288091906143ba565b1561289d5760405162461bcd60e51b81526004016107739061462e565b6000546001600160a01b031633146128ec5760405162461bcd60e51b815260206004820152601260248201527126a61d22231d2727aa2fa127a92927aba2a960711b6044820152606401610773565b816001600160a01b03167f7578fe8c4d9f6fc38fdad20d219b0ce47d38bbf8a72bdb26867809f24119363d8460405161292791815260200190565b60405180910390a2600061293a846112fb565b9050801561297b5760045460009061295a906001600160a01b0316611aee565b905061297781831161296d576000611343565b6104a78284614774565b9250505b83600f600082825461298d9190614774565b90915550506005546129a9906001600160a01b0316848661398b565b6129ed5760405162461bcd60e51b815260206004820152601560248201527413530e91118e9514905394d1915497d19052531151605a1b6044820152606401610773565b6129f5613c6d565b612a415760405162461bcd60e51b815260206004820152601d60248201527f4d4c3a44463a494e53554646494349454e545f434f4c4c41544552414c0000006044820152606401610773565b5092915050565b60008054819081906001600160a01b0316331415612d4157612a68612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015612aa057600080fd5b505afa158015612ab4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ad891906143ba565b15612af55760405162461bcd60e51b81526004016107739061462e565b831580612b155750600554612b15906001600160a01b03163330876138db565b612b615760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a434c3a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b60125442811015612bac5760405162461bcd60e51b81526020600482015260156024820152744d4c3a434c3a5041594d454e545f49535f4c41544560581b6044820152606401610773565b612bb4613a18565b612bbc6123e6565b50600060168190559195509350612bd3848661471b565b6005549091508190612bed906001600160a01b0316611aee565b600f54612bfa919061471b565b612c049190614774565b600f55601354612c1390613a28565b9250612c1d613952565b60408051868152602081018690529081018490527f4acb957de3799dd3d95bb7da6bcfbca6f5a33812d69ad37816ed87e79b6327d69060600160405180910390a1600554600154612c7b916001600160a01b0390811691168361398b565b612c975760405162461bcd60e51b8152600401610773906145ff565b60015460405163b5add71760e01b8152600481018790526024810186905260448101849052600060648201526001600160a01b039091169063b5add71790608401600060405180830381600087803b158015612cf257600080fd5b505af1158015612d06573d6000803e3d6000fd5b50506001546040518481526001600160a01b0390911692506000805160206147cd833981519152915060200160405180910390a25050611add565b600f54612d4c612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015612d8457600080fd5b505afa158015612d98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dbc91906143ba565b15612dd95760405162461bcd60e51b81526004016107739061462e565b841580612df95750600554612df9906001600160a01b03163330886138db565b612e455760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a434c3a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b60125442811015612e905760405162461bcd60e51b81526020600482015260156024820152744d4c3a434c3a5041594d454e545f49535f4c41544560581b6044820152606401610773565b612e98613a18565b612ea06123e6565b50600060168190559196509450612eb7858761471b565b6005549091508190612ed1906001600160a01b0316611aee565b600f54612ede919061471b565b612ee89190614774565b600f55601354612ef790613a28565b9350612f01613952565b60408051878152602081018790529081018590527f4acb957de3799dd3d95bb7da6bcfbca6f5a33812d69ad37816ed87e79b6327d69060600160405180910390a1600554600154612f5f916001600160a01b0390811691168361398b565b612f7b5760405162461bcd60e51b8152600401610773906145ff565b60015460405163b5add71760e01b8152600481018890526024810187905260448101849052600060648201526001600160a01b039091169063b5add71790608401600060405180830381600087803b158015612fd657600080fd5b505af1158015612fea573d6000803e3d6000fd5b50506001546040518481526001600160a01b0390911692506000805160206147cd833981519152915060200160405180910390a2505080600f541015611adb5760405162461bcd60e51b81526020600482015260166024820152754d4c3a43414e4e4f545f5553455f4452415741424c4560501b6044820152606401610773565b60006130756141e1565b61307d6141ff565b61309e42601254600754601454600e54601354600854600a54600b54613cfa565b91959094509092509050565b6130b2612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b1580156130ea57600080fd5b505afa1580156130fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061312291906143ba565b1561313f5760405162461bcd60e51b81526004016107739061462e565b6000546001600160a01b0316331461318e5760405162461bcd60e51b815260206004820152601260248201527126a61d29219d2727aa2fa127a92927aba2a960711b6044820152606401610773565b806001600160a01b03167f97b446ee2df422b7273fe6d658674835f9de3319d131c229f9a2f8ed62a76193836040516131c991815260200190565b60405180910390a281601160008282546131e39190614774565b90915550506004546131ff906001600160a01b0316828461398b565b6132435760405162461bcd60e51b815260206004820152601560248201527413530e9490ce9514905394d1915497d19052531151605a1b6044820152606401610773565b61324b613c6d565b6132975760405162461bcd60e51b815260206004820152601d60248201527f4d4c3a52433a494e53554646494349454e545f434f4c4c41544552414c0000006044820152606401610773565b5050565b6012546001546001600160a01b031633146132eb5760405162461bcd60e51b815260206004820152601060248201526f26a61d24a61d2727aa2fa622a72222a960811b6044820152606401610773565b60008142116132fa57426132fc565b815b90507f54c4bb2a061eac3afc1daf65bd7a75a0cdb4ac02824757f40019dff2da5849358160405161332f91815260200190565b60405180910390a1601255601a55565b6133476138ac565b6001600160a01b0316336001600160a01b03161461339b5760405162461bcd60e51b81526020600482015260116024820152704d4c3a53493a4e4f545f464143544f525960781b6044820152606401610773565b6001600160a01b03167f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b50565b600180546001600160a01b0319166001600160a01b03831690811790915560009061342f5760405162461bcd60e51b815260206004820152601460248201527326a61d23261d24a72b20a624a22fa622a72222a960611b6044820152606401610773565b6000826001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b15801561346a57600080fd5b505afa15801561347e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134a29190614283565b90506134ac612715565b604051636167f93160e01b81526b2627a0a72fa6a0a720a3a2a960a11b60048201526001600160a01b0383811660248301529190911690636167f9319060440160206040518083038186803b15801561350457600080fd5b505afa158015613518573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061353c91906143ba565b6135805760405162461bcd60e51b81526020600482015260156024820152744d4c3a464c3a494e56414c49445f464143544f525960581b6044820152606401610773565b6040516335a2735f60e11b81526001600160a01b038481166004830152821690636b44e6be9060240160206040518083038186803b1580156135c157600080fd5b505afa1580156135d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135f991906143ba565b61363e5760405162461bcd60e51b81526020600482015260166024820152754d4c3a464c3a494e56414c49445f494e5354414e434560501b6044820152606401610773565b60125415801561364f575060135415155b61368f5760405162461bcd60e51b81526020600482015260116024820152704d4c3a464c3a4c4f414e5f41435449564560781b6044820152606401610773565b600554600754600d546019546001600160a01b03938416936136b691859116600019613e90565b6136f75760405162461bcd60e51b815260206004820152601260248201527113530e91930e9054141493d59157d190525360721b6044820152606401610773565b6019546040516377cf0ddb60e11b815260048101839052602481018490526001600160a01b039091169063ef9e1bb690604401600060405180830381600087803b15801561374457600080fd5b505af1158015613758573d6000803e3d6000fd5b50506019546040516327d180b560e01b81526001600160a01b038781166004830152602482018690526000945090911691506327d180b590604401602060405180830381600087803b1580156137ad57600080fd5b505af11580156137c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137e591906143f5565b90506137f18183614774565b600f5560006137ff85611aee565b146138455760405162461bcd60e51b81526020600482015260166024820152754d4c3a464c3a554e45585045435445445f46554e445360501b6044820152606401610773565b866001600160a01b03167fcd909ec339185c4598a4096e174308fbdf136d117f230960f873a2f2e81f63af8360148190559750878542613885919061471b565b60128190556040805192835260208301919091520160405180910390a25050505050919050565b60006138d67f7a45a402e4cb6e08ebc196f20f66d5d30e67285a2a8aa80503fa409e727a4af15490565b919050565b6040516001600160a01b03808516602483015283166044820152606481018290526000906139499086906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613f0c565b95945050505050565b60006006819055600781905560088190556009819055600a819055600b819055600e819055601281905560138190556014819055601a55565b6040516001600160a01b0383166024820152604481018290526000906139be90859063a9059cbb60e01b90606401613912565b90505b9392505050565b600083851115613a0d57826001816139e08789614774565b6139ea9086614755565b6139f4919061471b565b6139fe9190614774565b613a089190614733565b613949565b506000949350505050565b601a54613a2157565b6000601a55565b6005546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a082319060240160206040518083038186803b158015613a7057600080fd5b505afa158015613a84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aa891906143f5565b6019546005546040516376bd6e5160e01b81526001600160a01b0391821660048201526024810187905292935016906376bd6e5190604401602060405180830381600087803b158015613afa57600080fd5b505af1158015613b0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b3291906143f5565b506005546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015613b7757600080fd5b505afa158015613b8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613baf91906143f5565b905080821115613be257613bc38183614774565b925082600f6000828254613bd79190614774565b90915550613c039050565b613bec8282614774565b600f6000828254613bfd919061471b565b90915550505b5050919050565b60006138d67f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b600084848484604051602001613c4d949392919061459e565b604051602081830303815290604052805190602001209050949350505050565b6000613c83601454600f54600d54600c546139c8565b6011541015905090565b600083613c9a8a8c61471b565b1015613ca857506000613ced565b6000613cb48a86614774565b613cbe908c614774565b9050613ccd898989848a613fac565b9250613cdf90508b8a8988888861409f565b613ce9908361471b565b9150505b9998505050505050505050565b6000613d046141e1565b613d0c6141ff565b613d198989888d8b613fac565b8352925060018714613d2b5782613d2d565b885b9250613d3d8c8a888e898961409f565b8260016020020152601654826002602002015260195460405163e0b58e0d60e01b8152306004820152600160248201526000918291829182916001600160a01b039091169063e0b58e0d9060440160806040518083038186803b158015613da357600080fd5b505afa158015613db7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ddb9190614466565b93509350935093508284613def919061471b565b8552613dfb818361471b565b602086015250949e939d50919b50919950505050505050505050565b6000833b80613e2a5760009150506139c1565b846001600160a01b03168484604051613e44929190614553565b600060405180830381855af49150503d8060008114613e7f576040519150601f19603f3d011682016040523d82523d6000602084013e613e84565b606091505b50909695505050505050565b6040516001600160a01b038316602482015260006044820181905290613ec390859063095ea7b360e01b90606401613912565b613ecf575060006139c1565b81613edc575060016139c1565b6040516001600160a01b0384166024820152604481018390526139be90859063095ea7b360e01b90606401613912565b60006001600160a01b0383163b613f2557506000611bbc565b6060836001600160a01b031683604051613f3f9190614563565b6000604051808303816000865af19150503d8060008114613f7c576040519150601f19603f3d011682016040523d82523d6000602084013e613f81565b606091505b50909250905081801561133b57508051158061133b57508080602001905181019061133b91906143ba565b6000806000613fbb868661413b565b90506000613fe3613fd483670de0b6b3a764000061471b565b86670de0b6b3a7640000614156565b9050670de0b6b3a764000081116140165784613fff898b614774565b6140099190614733565b6000935093505050614095565b600061402a670de0b6b3a764000083614774565b838a670de0b6b3a764000061403f868f614755565b6140499190614733565b6140539190614774565b61405d9190614755565b6140679190614733565b90506140748a89896141b8565b93508381101561408557600061408f565b61408f8482614774565b94505050505b9550959350505050565b60008387116140b057506000614131565b6000620151806140c0868a614774565b6140cd906201517f61471b565b6140d79190614733565b6140e49062015180614755565b90506140fa876140f4858961471b565b836141b8565b614104908361471b565b9150670de0b6b3a76400006141198886614755565b6141239190614733565b61412d908361471b565b9150505b9695505050505050565b60006301e1338061414c8385614755565b6139c19190614733565b6000600183166141665781614168565b835b90505b60019290921c9182156139c157816141838580614755565b61418d9190614733565b93506001831661419c5761416b565b816141a78583614755565b6141b19190614733565b905061416b565b6000670de0b6b3a76400006141cd848461413b565b6141d79086614755565b6139be9190614733565b60405180606001604052806003906020820280368337509192915050565b60405180604001604052806002906020820280368337509192915050565b60008083601f84011261422f57600080fd5b50813567ffffffffffffffff81111561424757600080fd5b60208301915083602082850101111561425f57600080fd5b9250929050565b60006020828403121561427857600080fd5b81356139c1816147b7565b60006020828403121561429557600080fd5b81516139c1816147b7565b600080604083850312156142b357600080fd5b82356142be816147b7565b915060208301356142ce816147b7565b809150509250929050565b6000806000604084860312156142ee57600080fd5b83356142f9816147b7565b9250602084013567ffffffffffffffff81111561431557600080fd5b6143218682870161421d565b9497909650939450505050565b6000806000806060858703121561434457600080fd5b843561434f816147b7565b935060208501359250604085013567ffffffffffffffff8082111561437357600080fd5b818701915087601f83011261438757600080fd5b81358181111561439657600080fd5b8860208260051b85010111156143ab57600080fd5b95989497505060200194505050565b6000602082840312156143cc57600080fd5b815180151581146139c157600080fd5b6000602082840312156143ee57600080fd5b5035919050565b60006020828403121561440757600080fd5b5051919050565b6000806040838503121561442157600080fd5b8235915060208301356142ce816147b7565b60008060006040848603121561444857600080fd5b83359250602084013567ffffffffffffffff81111561431557600080fd5b6000806000806080858703121561447c57600080fd5b505082516020840151604085015160609095015191969095509092509050565b818352600060208085019450848460051b86018460005b8781101561451d5783830389528135601e198836030181126144d457600080fd5b8701803567ffffffffffffffff8111156144ed57600080fd5b8036038913156144fc57600080fd5b614509858289850161452a565b9a87019a94505050908401906001016144b3565b5090979650505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8183823760009101908152919050565b6000825160005b81811015614584576020818601810151858301520161456a565b81811115614593576000828501525b509190910192915050565b60018060a01b038516815283602082015260606040820152600061413160608301848661449c565b85815260018060a01b03851660208201528360408201526080606082015260006145f460808301848661449c565b979650505050505050565b60208082526015908201527413530e93540e9514905394d1915497d19052531151605a1b604082015260600190565b602080825260119082015270130e941493d513d0d3d317d4105554d151607a1b604082015260600190565b83815260c0810160208083018560005b600381101561468657815183529183019190830190600101614669565b505050608083018460005b60028110156146ae57815183529183019190830190600101614691565b50505050949350505050565b83815260406020820152600061394960408301848661452a565b6000808335601e198436030181126146eb57600080fd5b83018035915067ffffffffffffffff82111561470657600080fd5b60200191503681900382131561425f57600080fd5b6000821982111561472e5761472e61478b565b500190565b60008261475057634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561476f5761476f61478b565b500290565b6000828210156147865761478661478b565b500390565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6001600160a01b03811681146133c857600080fdfe6bd56533ce1c8ea03f7b858ac441b5a86d140a793a7c9e3faecbbe517c2c8791a26469706673582212204a309c37cbae06ae4046df36b05aa1ac996ec534c14d5709848916960b7ff5da64736f6c63430008070033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103835760003560e01c80637c3a00fd116101de578063ba5d30781161010f578063d05951a0116100ad578063d4754d5f1161007c578063d4754d5f146106a8578063d784d426146106b0578063d8dfeb45146106c3578063e48f6faf146106cb57600080fd5b8063d05951a01461065a578063d0fb02031461066d578063d3bcd5e21461067e578063d41ddc961461069557600080fd5b8063c3124525116100e9578063c312452514610624578063c3fbb6fd1461062c578063c45a01551461063f578063ccc044841461064757600080fd5b8063ba5d307814610603578063ba83276b1461060b578063bcead63e1461061357600080fd5b8063a29d7aab1161017c578063acb522b411610156578063acb522b4146105d8578063b86a513e146105eb578063b96b5c99146105f3578063b9b1f4e3146105fb57600080fd5b8063a29d7aab146105ac578063a97d1161146105bf578063aabaecd6146105c757600080fd5b80638ffc9215116101b85780638ffc92151461058157806396663914146105895780639e10320b14610591578063a06db7dc146105a457600080fd5b80637c3a00fd146105555780637df1f1b91461055d57806387accaf11461056e57600080fd5b806345755dd6116102b85780636174b27211610256578063712b772f11610230578063712b772f1461052a57806375a206761461053d57806377b3c55c146105455780637a0e6fa11461054d57600080fd5b80636174b272146104f3578063700f50061461050657806370a10c891461051757600080fd5b806350f2012f1161029257806350f2012f146104995780635114cb52146104ac5780635c60da1b146104da5780635eeb53b4146104e257600080fd5b806345755dd61461044b57806347350e9f1461045e5780634eac42351461048657600080fd5b8063227000371161032557806339ba9f86116102ff57806339ba9f861461041757806339d65834146104285780633b99bcee146104305780634003f34d1461044357600080fd5b806322700037146103f4578063267f4ac3146103fc5780632ead10981461040f57600080fd5b80630fe3d9b7116103615780630fe3d9b7146103c9578063144ee4b5146103d15780631cc1cf46146103d95780631f3f19ab146103e157600080fd5b806301daa38f146103885780630895326f146103925780630c340a24146103a9575b600080fd5b6103906106de565b005b6013545b6040519081526020015b60405180910390f35b6103b161081d565b6040516001600160a01b0390911681526020016103a0565b61039061089c565b6103906109d4565b600754610396565b6103906103ef366004614266565b610aec565b601a54610396565b61039061040a366004614266565b610cee565b600b54610396565b6005546001600160a01b03166103b1565b600954610396565b61039061043e366004614433565b610d8a565b601254610396565b6103966104593660046143dc565b610fa0565b61047161046c366004614266565b61110a565b604080519283526020830191909152016103a0565b6103966104943660046143dc565b6112fb565b6103966104a73660046143dc565b611343565b6104bf6104ba3660046143dc565b6114a4565b604080519384526020840192909252908201526060016103a0565b6103b1611ae4565b6003546001600160a01b03166103b1565b610396610501366004614266565b611aee565b6002546001600160a01b03166103b1565b61039661052536600461432e565b611bc2565b6103966105383660046142a0565b6120d6565b600c54610396565b600a54610396565b61039661220d565b600854610396565b6000546001600160a01b03166103b1565b61039661057c36600461432e565b612249565b600d54610396565b6104bf6123e6565b61039661059f3660046143dc565b6124e3565b600654610396565b601a5460405190151581526020016103a0565b601654610396565b6004546001600160a01b03166103b1565b6103966105e636600461432e565b612506565b600e54610396565b6104bf612699565b600f54610396565b601454610396565b601554610396565b6001546001600160a01b03166103b1565b6103b1612715565b61039061063a3660046142d9565b612757565b6103b16127fc565b61039661065536600461440e565b612806565b6104bf6106683660046143dc565b612a48565b6019546001600160a01b03166103b1565b61068661306b565b6040516103a093929190614659565b6103906106a336600461440e565b6130aa565b61039061329b565b6103906106be366004614266565b61333f565b601154610396565b6103966106d9366004614266565b6133cb565b6106e6612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561071e57600080fd5b505afa158015610732573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061075691906143ba565b1561077c5760405162461bcd60e51b81526004016107739061462e565b60405180910390fd5b6002546001600160a01b031633146107d65760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a41423a4e4f545f50454e44494e475f424f52524f5745520000000000006044820152606401610773565b600280546001600160a01b031990811690915560008054339216821781556040517f29bac0ac2b15405bfcc160bb74b6ae7a559b7674ce33db80785ada73e38204d29190a2565b6000610827612715565b6001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b15801561085f57600080fd5b505afa158015610873573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108979190614283565b905090565b6108a4612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b1580156108dc57600080fd5b505afa1580156108f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061091491906143ba565b156109315760405162461bcd60e51b81526004016107739061462e565b6003546001600160a01b0316331461098b5760405162461bcd60e51b815260206004820152601860248201527f4d4c3a414c3a4e4f545f50454e44494e475f4c454e44455200000000000000006044820152606401610773565b600380546001600160a01b031990811690915560018054339216821790556040517fd6165838d2e3db87aa1002b548048673fc6427eefbd1b914e100f3a0deae23e390600090a2565b601a546001546001600160a01b03163314610a255760405162461bcd60e51b815260206004820152601160248201527026a61d2926249d2727aa2fa622a72222a960791b6044820152606401610773565b80610a685760405162461bcd60e51b815260206004820152601360248201527213530e9493124e9393d517d253541052549151606a1b6044820152606401610773565b80421115610aab5760405162461bcd60e51b815260206004820152601060248201526f4d4c3a524c493a504153545f4441544560801b6044820152606401610773565b60128190556000601a556040518181527f962d5a38dc48af765ecb4f177a88713b592b5bf59e64f497ab33af13b7c0e9de906020015b60405180910390a150565b610af4612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015610b2c57600080fd5b505afa158015610b40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6491906143ba565b15610b815760405162461bcd60e51b81526004016107739061462e565b6000546001600160a01b03163314610bd15760405162461bcd60e51b815260206004820152601360248201527226a61d29a8211d2727aa2fa127a92927aba2a960691b6044820152606401610773565b610bd9612715565b60405163eaf6e48360e01b81526001600160a01b038381166004830152919091169063eaf6e4839060240160206040518083038186803b158015610c1c57600080fd5b505afa158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5491906143ba565b610ca05760405162461bcd60e51b815260206004820152601760248201527f4d4c3a5350423a494e56414c49445f424f52524f5745520000000000000000006044820152606401610773565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f10f06072822ef73860fedb88933f968d20bb4aadce8a8d360d1124cb6ce1e0b290602001610ae1565b6001546001600160a01b03163314610d3c5760405162461bcd60e51b815260206004820152601160248201527026a61d29a8261d2727aa2fa622a72222a960791b6044820152606401610773565b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527fa3ab02442c80a4102475683f16513c9139a89142be9db9804edfcfbb379fc49290602001610ae1565b610d92612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015610dca57600080fd5b505afa158015610dde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0291906143ba565b15610e1f5760405162461bcd60e51b81526004016107739061462e565b610e27612715565b6001600160a01b031663be7c13f76040518163ffffffff1660e01b815260040160206040518083038186803b158015610e5f57600080fd5b505afa158015610e73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e979190614283565b6001600160a01b0316336001600160a01b031614610ef75760405162461bcd60e51b815260206004820152601760248201527f4d4c3a553a4e4f545f53454355524954595f41444d494e0000000000000000006044820152606401610773565b7faaaa7ee6b0c2f4ee1fa7312c7d5b3623a434da5a1a9ce3cb6e629caa23454ab6838383604051610f2a939291906146ba565b60405180910390a1610f3a6138ac565b6001600160a01b031663fe69f7088484846040518463ffffffff1660e01b8152600401610f69939291906146ba565b600060405180830381600087803b158015610f8357600080fd5b505af1158015610f97573d6000803e3d6000fd5b50505050505050565b6000610faa612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015610fe257600080fd5b505afa158015610ff6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101a91906143ba565b156110375760405162461bcd60e51b81526004016107739061462e565b8115806110575750600554611057906001600160a01b03163330856138db565b6110a35760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a52463a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b6005546110b8906001600160a01b0316611aee565b905080600f60008282546110cc919061471b565b90915550506040518181527f278e51d3323fbf18b9fb8df3f8b97e31b145bc1146c52e764cf4aa1bfc4ba17d906020015b60405180910390a1919050565b60015460009081906001600160a01b0316331461115b5760405162461bcd60e51b815260206004820152600f60248201526e26a61d291d2727aa2fa622a72222a960891b6044820152606401610773565b60125480158015906111785750600654611175908261471b565b42115b6111ba5760405162461bcd60e51b815260206004820152601360248201527213530e948e9393d517d25397d1115190555315606a1b6044820152606401610773565b6111c2613952565b60006011819055600f8190556004546001600160a01b0316906111e482611aee565b94508414806111f957506111f981868661398b565b61123e5760405162461bcd60e51b815260206004820152601660248201527513530e948e90d7d514905394d1915497d1905253115160521b6044820152606401610773565b6005546001600160a01b0316600061125582611aee565b945084148061126a575061126a81878661398b565b6112af5760405162461bcd60e51b815260206004820152601660248201527513530e948e9197d514905394d1915497d1905253115160521b6044820152606401610773565b60408051868152602081018690526001600160a01b038816917f027e623aab0b174da270ff529cad1c54f09182651e07437d2ac557929b9e5b49910160405180910390a2505050915091565b60008061131d60145484600f546113129190614774565b600d54600c546139c8565b60115490915080821161133157600061133b565b61133b8183614774565b949350505050565b600061134d612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561138557600080fd5b505afa158015611399573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113bd91906143ba565b156113da5760405162461bcd60e51b81526004016107739061462e565b8115806113fa57506004546113fa906001600160a01b03163330856138db565b6114465760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a50433a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b60045461145b906001600160a01b0316611aee565b9050806011600082825461146f919061471b565b90915550506040518181527f437d44b2c697fb69e2b2f25f57fd844e376c25ed28ed5a9c4be88aa1e5c87d12906020016110fd565b60008054819081906001600160a01b03163314156117a7576114c4612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b1580156114fc57600080fd5b505afa158015611510573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061153491906143ba565b156115515760405162461bcd60e51b81526004016107739061462e565b8315806115715750600554611571906001600160a01b03163330876138db565b6115bd5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a4d503a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b6115c5613a18565b6115cd612699565b506000601681905591945092506115e4838561471b565b60055490915081906115fe906001600160a01b0316611aee565b600f5461160b919061471b565b6116159190614774565b600f556116226001613a28565b60135460125491935090600060018314156116445761163f613952565b611681565b600754611651908361471b565b905080601281905550866014600082825461166c9190614774565b9091555061167d9050600184614774565b6013555b60408051888152602081018890529081018690527fcf358e925a8e033c6db877f18d10df6f21cd04ef165537bad5fc814eb23af9609060600160405180910390a16005546001546116df916001600160a01b0390811691168661398b565b6116fb5760405162461bcd60e51b8152600401610773906145ff565b60015460405163b5add71760e01b8152600481018990526024810188905260448101849052606481018390526001600160a01b039091169063b5add71790608401600060405180830381600087803b15801561175657600080fd5b505af115801561176a573d6000803e3d6000fd5b50506001546040518781526001600160a01b0390911692506000805160206147cd833981519152915060200160405180910390a250505050611add565b600f546117b2612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b1580156117ea57600080fd5b505afa1580156117fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182291906143ba565b1561183f5760405162461bcd60e51b81526004016107739061462e565b84158061185f575060055461185f906001600160a01b03163330886138db565b6118ab5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a4d503a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b6118b3613a18565b6118bb612699565b506000601681905591955093506118d2848661471b565b60055490915081906118ec906001600160a01b0316611aee565b600f546118f9919061471b565b6119039190614774565b600f556119106001613a28565b60135460125491945090600060018314156119325761192d613952565b61196f565b60075461193f908361471b565b905080601281905550876014600082825461195a9190614774565b9091555061196b9050600184614774565b6013555b60408051898152602081018990529081018790527fcf358e925a8e033c6db877f18d10df6f21cd04ef165537bad5fc814eb23af9609060600160405180910390a16005546001546119cd916001600160a01b0390811691168661398b565b6119e95760405162461bcd60e51b8152600401610773906145ff565b60015460405163b5add71760e01b8152600481018a90526024810189905260448101849052606481018390526001600160a01b039091169063b5add71790608401600060405180830381600087803b158015611a4457600080fd5b505af1158015611a58573d6000803e3d6000fd5b50506001546040518781526001600160a01b0390911692506000805160206147cd833981519152915060200160405180910390a25050505080600f541015611adb5760405162461bcd60e51b81526020600482015260166024820152754d4c3a43414e4e4f545f5553455f4452415741424c4560501b6044820152606401610773565b505b9193909250565b6000610897613c0a565b6005546000906001600160a01b03838116911614611b0d576000611b11565b600f545b6004546001600160a01b03848116911614611b2d576000611b31565b6011545b6040516370a0823160e01b81523060048201526001600160a01b038516906370a082319060240160206040518083038186803b158015611b7057600080fd5b505afa158015611b84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba891906143f5565b611bb29190614774565b611bbc9190614774565b92915050565b6001546000906001600160a01b03163314611c135760405162461bcd60e51b815260206004820152601160248201527026a61d20a72a1d2727aa2fa622a72222a960791b6044820152606401610773565b611c1f85858585613c34565b90508060155414611c725760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a414e543a434f4d4d49544d454e545f4d49534d415443480000000000006044820152606401610773565b6001600160a01b0385163b611cc95760405162461bcd60e51b815260206004820152601960248201527f4d4c3a414e543a494e56414c49445f524546494e414e434552000000000000006044820152606401610773565b83421115611d195760405162461bcd60e51b815260206004820152601960248201527f4d4c3a414e543a455850495245445f434f4d4d49544d454e54000000000000006044820152606401610773565b600754601254600d54600082611d2f854261471b565b10611d4d57611d3e8484614774565b611d489042614774565b611d50565b60005b6019546040516341a703bb60e01b815260048101859052602481018390529192506001600160a01b03169081906341a703bb90604401600060405180830381600087803b158015611da057600080fd5b505af1158015611db4573d6000803e3d6000fd5b505050506000611dc3426124e3565b90508060166000828254611dd7919061471b565b9091555050600060158190555b88811015611eb45760008c6001600160a01b03168b8b84818110611e0a57611e0a6147a1565b9050602002810190611e1c91906146d4565b604051611e2a929190614553565b600060405180830381855af49150503d8060008114611e65576040519150601f19603f3d011682016040523d82523d6000602084013e611e6a565b606091505b5050905080611eab5760405162461bcd60e51b815260206004820152600d60248201526c13530e9053950e919052531151609a1b6044820152606401610773565b50600101611de4565b507f7150c332bd889236b6ab42cc34f0853631ceb58827f58a8697b682f13e390a8c878c8c8c8c604051611eec9594939291906145c6565b60405180910390a1600554600d5460075497506001600160a01b0390911690611f15884261471b565b601255611f20613a18565b6040516377cf0ddb60e11b815260048101829052602481018990526001600160a01b0385169063ef9e1bb690604401600060405180830381600087803b158015611f6957600080fd5b505af1158015611f7d573d6000803e3d6000fd5b50506040516327d180b560e01b81526001600160a01b03858116600483015260248201859052871692506327d180b59150604401602060405180830381600087803b158015611fcb57600080fd5b505af1158015611fdf573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061200391906143f5565b600f60008282546120149190614774565b909155506120229050613c6d565b61206e5760405162461bcd60e51b815260206004820152601e60248201527f4d4c3a414e543a494e53554646494349454e545f434f4c4c41544552414c00006044820152606401610773565b600061207983611aee565b146120c65760405162461bcd60e51b815260206004820152601760248201527f4d4c3a414e543a554e45585045435445445f46554e44530000000000000000006044820152606401610773565b5050505050505050949350505050565b60006120e0612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561211857600080fd5b505afa15801561212c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061215091906143ba565b1561216d5760405162461bcd60e51b81526004016107739061462e565b816001600160a01b0316836001600160a01b03167ff1f6a55e7ad487ac8dd8e1d4517348d3b410a7a0bc405ef87b09078dc51b23b66121ab86611aee565b60405181815290945060200160405180910390a36121ca83838361398b565b611bbc5760405162461bcd60e51b815260206004820152601460248201527313530e94ce9514905394d1915497d1905253115160621b6044820152606401610773565b600080612224601454600f54600d54600c546139c8565b601154909150818111612238576000612242565b6122428282614774565b9250505090565b6000612253612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561228b57600080fd5b505afa15801561229f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c391906143ba565b156122e05760405162461bcd60e51b81526004016107739061462e565b6000546001600160a01b031633146123305760405162461bcd60e51b815260206004820152601360248201527226a61d28272a1d2727aa2fa127a92927aba2a960691b6044820152606401610773565b428410156123805760405162461bcd60e51b815260206004820152601760248201527f4d4c3a504e543a494e56414c49445f444541444c494e450000000000000000006044820152606401610773565b7ff94d2f0322894aaf1bce14561461a8b8b6c9b11a77bbe80f20b804da8a95e4b7826123ad5760006123b9565b6123b986868686613c34565b6015819055915081868686866040516123d69594939291906145c6565b60405180910390a1949350505050565b60195460135460405163e0b58e0d60e01b815230600482015260248101919091526000918291829182918291829182916001600160a01b03169063e0b58e0d9060440160806040518083038186803b15801561244157600080fd5b505afa158015612455573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124799190614466565b93509350935093508083838661248f919061471b565b612499919061471b565b6124a3919061471b565b9450601654670de0b6b3a76400006009546014549950896124c49190614755565b6124ce9190614733565b6124d8919061471b565b955050505050909192565b6000611bbc82600754601454600e54600854601354601254600a54600b54613c8d565b6000612510612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561254857600080fd5b505afa15801561255c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061258091906143ba565b1561259d5760405162461bcd60e51b81526004016107739061462e565b6000546001600160a01b03163314806125c057506001546001600160a01b031633145b6125fd5760405162461bcd60e51b815260206004820152600e60248201526d09a9874a49ca8749c9ebe82aaa8960931b6044820152606401610773565b61260985858585613c34565b9050806015541461265c5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a524e543a434f4d4d49544d454e545f4d49534d415443480000000000006044820152606401610773565b60006015556040517f47244a449377da5fd10e98d86d118dee442e842fc34f05179c973cfcff6acba7906123d690839088908890889088906145c6565b60008060006126a66141e1565b6126ae6141ff565b6126cf42601254600754601454600e54601354600854600a54600b54613cfa565b604082015160208301518351949950929550909350916126ef919061471b565b6126f9919061471b565b6020820151825191955061270c9161471b565b92505050909192565b600061271f6138ac565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561085f57600080fd5b61275f6138ac565b6001600160a01b0316336001600160a01b0316146127b25760405162461bcd60e51b815260206004820152601060248201526f4d4c3a4d3a4e4f545f464143544f525960801b6044820152606401610773565b6127bd838383613e17565b6127f75760405162461bcd60e51b815260206004820152600b60248201526a13530e934e91905253115160aa1b6044820152606401610773565b505050565b60006108976138ac565b6000612810612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561284857600080fd5b505afa15801561285c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061288091906143ba565b1561289d5760405162461bcd60e51b81526004016107739061462e565b6000546001600160a01b031633146128ec5760405162461bcd60e51b815260206004820152601260248201527126a61d22231d2727aa2fa127a92927aba2a960711b6044820152606401610773565b816001600160a01b03167f7578fe8c4d9f6fc38fdad20d219b0ce47d38bbf8a72bdb26867809f24119363d8460405161292791815260200190565b60405180910390a2600061293a846112fb565b9050801561297b5760045460009061295a906001600160a01b0316611aee565b905061297781831161296d576000611343565b6104a78284614774565b9250505b83600f600082825461298d9190614774565b90915550506005546129a9906001600160a01b0316848661398b565b6129ed5760405162461bcd60e51b815260206004820152601560248201527413530e91118e9514905394d1915497d19052531151605a1b6044820152606401610773565b6129f5613c6d565b612a415760405162461bcd60e51b815260206004820152601d60248201527f4d4c3a44463a494e53554646494349454e545f434f4c4c41544552414c0000006044820152606401610773565b5092915050565b60008054819081906001600160a01b0316331415612d4157612a68612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015612aa057600080fd5b505afa158015612ab4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ad891906143ba565b15612af55760405162461bcd60e51b81526004016107739061462e565b831580612b155750600554612b15906001600160a01b03163330876138db565b612b615760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a434c3a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b60125442811015612bac5760405162461bcd60e51b81526020600482015260156024820152744d4c3a434c3a5041594d454e545f49535f4c41544560581b6044820152606401610773565b612bb4613a18565b612bbc6123e6565b50600060168190559195509350612bd3848661471b565b6005549091508190612bed906001600160a01b0316611aee565b600f54612bfa919061471b565b612c049190614774565b600f55601354612c1390613a28565b9250612c1d613952565b60408051868152602081018690529081018490527f4acb957de3799dd3d95bb7da6bcfbca6f5a33812d69ad37816ed87e79b6327d69060600160405180910390a1600554600154612c7b916001600160a01b0390811691168361398b565b612c975760405162461bcd60e51b8152600401610773906145ff565b60015460405163b5add71760e01b8152600481018790526024810186905260448101849052600060648201526001600160a01b039091169063b5add71790608401600060405180830381600087803b158015612cf257600080fd5b505af1158015612d06573d6000803e3d6000fd5b50506001546040518481526001600160a01b0390911692506000805160206147cd833981519152915060200160405180910390a25050611add565b600f54612d4c612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015612d8457600080fd5b505afa158015612d98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dbc91906143ba565b15612dd95760405162461bcd60e51b81526004016107739061462e565b841580612df95750600554612df9906001600160a01b03163330886138db565b612e455760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a434c3a5452414e534645525f46524f4d5f4641494c45440000000000006044820152606401610773565b60125442811015612e905760405162461bcd60e51b81526020600482015260156024820152744d4c3a434c3a5041594d454e545f49535f4c41544560581b6044820152606401610773565b612e98613a18565b612ea06123e6565b50600060168190559196509450612eb7858761471b565b6005549091508190612ed1906001600160a01b0316611aee565b600f54612ede919061471b565b612ee89190614774565b600f55601354612ef790613a28565b9350612f01613952565b60408051878152602081018790529081018590527f4acb957de3799dd3d95bb7da6bcfbca6f5a33812d69ad37816ed87e79b6327d69060600160405180910390a1600554600154612f5f916001600160a01b0390811691168361398b565b612f7b5760405162461bcd60e51b8152600401610773906145ff565b60015460405163b5add71760e01b8152600481018890526024810187905260448101849052600060648201526001600160a01b039091169063b5add71790608401600060405180830381600087803b158015612fd657600080fd5b505af1158015612fea573d6000803e3d6000fd5b50506001546040518481526001600160a01b0390911692506000805160206147cd833981519152915060200160405180910390a2505080600f541015611adb5760405162461bcd60e51b81526020600482015260166024820152754d4c3a43414e4e4f545f5553455f4452415741424c4560501b6044820152606401610773565b60006130756141e1565b61307d6141ff565b61309e42601254600754601454600e54601354600854600a54600b54613cfa565b91959094509092509050565b6130b2612715565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b1580156130ea57600080fd5b505afa1580156130fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061312291906143ba565b1561313f5760405162461bcd60e51b81526004016107739061462e565b6000546001600160a01b0316331461318e5760405162461bcd60e51b815260206004820152601260248201527126a61d29219d2727aa2fa127a92927aba2a960711b6044820152606401610773565b806001600160a01b03167f97b446ee2df422b7273fe6d658674835f9de3319d131c229f9a2f8ed62a76193836040516131c991815260200190565b60405180910390a281601160008282546131e39190614774565b90915550506004546131ff906001600160a01b0316828461398b565b6132435760405162461bcd60e51b815260206004820152601560248201527413530e9490ce9514905394d1915497d19052531151605a1b6044820152606401610773565b61324b613c6d565b6132975760405162461bcd60e51b815260206004820152601d60248201527f4d4c3a52433a494e53554646494349454e545f434f4c4c41544552414c0000006044820152606401610773565b5050565b6012546001546001600160a01b031633146132eb5760405162461bcd60e51b815260206004820152601060248201526f26a61d24a61d2727aa2fa622a72222a960811b6044820152606401610773565b60008142116132fa57426132fc565b815b90507f54c4bb2a061eac3afc1daf65bd7a75a0cdb4ac02824757f40019dff2da5849358160405161332f91815260200190565b60405180910390a1601255601a55565b6133476138ac565b6001600160a01b0316336001600160a01b03161461339b5760405162461bcd60e51b81526020600482015260116024820152704d4c3a53493a4e4f545f464143544f525960781b6044820152606401610773565b6001600160a01b03167f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b50565b600180546001600160a01b0319166001600160a01b03831690811790915560009061342f5760405162461bcd60e51b815260206004820152601460248201527326a61d23261d24a72b20a624a22fa622a72222a960611b6044820152606401610773565b6000826001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b15801561346a57600080fd5b505afa15801561347e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134a29190614283565b90506134ac612715565b604051636167f93160e01b81526b2627a0a72fa6a0a720a3a2a960a11b60048201526001600160a01b0383811660248301529190911690636167f9319060440160206040518083038186803b15801561350457600080fd5b505afa158015613518573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061353c91906143ba565b6135805760405162461bcd60e51b81526020600482015260156024820152744d4c3a464c3a494e56414c49445f464143544f525960581b6044820152606401610773565b6040516335a2735f60e11b81526001600160a01b038481166004830152821690636b44e6be9060240160206040518083038186803b1580156135c157600080fd5b505afa1580156135d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135f991906143ba565b61363e5760405162461bcd60e51b81526020600482015260166024820152754d4c3a464c3a494e56414c49445f494e5354414e434560501b6044820152606401610773565b60125415801561364f575060135415155b61368f5760405162461bcd60e51b81526020600482015260116024820152704d4c3a464c3a4c4f414e5f41435449564560781b6044820152606401610773565b600554600754600d546019546001600160a01b03938416936136b691859116600019613e90565b6136f75760405162461bcd60e51b815260206004820152601260248201527113530e91930e9054141493d59157d190525360721b6044820152606401610773565b6019546040516377cf0ddb60e11b815260048101839052602481018490526001600160a01b039091169063ef9e1bb690604401600060405180830381600087803b15801561374457600080fd5b505af1158015613758573d6000803e3d6000fd5b50506019546040516327d180b560e01b81526001600160a01b038781166004830152602482018690526000945090911691506327d180b590604401602060405180830381600087803b1580156137ad57600080fd5b505af11580156137c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137e591906143f5565b90506137f18183614774565b600f5560006137ff85611aee565b146138455760405162461bcd60e51b81526020600482015260166024820152754d4c3a464c3a554e45585045435445445f46554e445360501b6044820152606401610773565b866001600160a01b03167fcd909ec339185c4598a4096e174308fbdf136d117f230960f873a2f2e81f63af8360148190559750878542613885919061471b565b60128190556040805192835260208301919091520160405180910390a25050505050919050565b60006138d67f7a45a402e4cb6e08ebc196f20f66d5d30e67285a2a8aa80503fa409e727a4af15490565b919050565b6040516001600160a01b03808516602483015283166044820152606481018290526000906139499086906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613f0c565b95945050505050565b60006006819055600781905560088190556009819055600a819055600b819055600e819055601281905560138190556014819055601a55565b6040516001600160a01b0383166024820152604481018290526000906139be90859063a9059cbb60e01b90606401613912565b90505b9392505050565b600083851115613a0d57826001816139e08789614774565b6139ea9086614755565b6139f4919061471b565b6139fe9190614774565b613a089190614733565b613949565b506000949350505050565b601a54613a2157565b6000601a55565b6005546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a082319060240160206040518083038186803b158015613a7057600080fd5b505afa158015613a84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aa891906143f5565b6019546005546040516376bd6e5160e01b81526001600160a01b0391821660048201526024810187905292935016906376bd6e5190604401602060405180830381600087803b158015613afa57600080fd5b505af1158015613b0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b3291906143f5565b506005546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015613b7757600080fd5b505afa158015613b8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613baf91906143f5565b905080821115613be257613bc38183614774565b925082600f6000828254613bd79190614774565b90915550613c039050565b613bec8282614774565b600f6000828254613bfd919061471b565b90915550505b5050919050565b60006138d67f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b600084848484604051602001613c4d949392919061459e565b604051602081830303815290604052805190602001209050949350505050565b6000613c83601454600f54600d54600c546139c8565b6011541015905090565b600083613c9a8a8c61471b565b1015613ca857506000613ced565b6000613cb48a86614774565b613cbe908c614774565b9050613ccd898989848a613fac565b9250613cdf90508b8a8988888861409f565b613ce9908361471b565b9150505b9998505050505050505050565b6000613d046141e1565b613d0c6141ff565b613d198989888d8b613fac565b8352925060018714613d2b5782613d2d565b885b9250613d3d8c8a888e898961409f565b8260016020020152601654826002602002015260195460405163e0b58e0d60e01b8152306004820152600160248201526000918291829182916001600160a01b039091169063e0b58e0d9060440160806040518083038186803b158015613da357600080fd5b505afa158015613db7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ddb9190614466565b93509350935093508284613def919061471b565b8552613dfb818361471b565b602086015250949e939d50919b50919950505050505050505050565b6000833b80613e2a5760009150506139c1565b846001600160a01b03168484604051613e44929190614553565b600060405180830381855af49150503d8060008114613e7f576040519150601f19603f3d011682016040523d82523d6000602084013e613e84565b606091505b50909695505050505050565b6040516001600160a01b038316602482015260006044820181905290613ec390859063095ea7b360e01b90606401613912565b613ecf575060006139c1565b81613edc575060016139c1565b6040516001600160a01b0384166024820152604481018390526139be90859063095ea7b360e01b90606401613912565b60006001600160a01b0383163b613f2557506000611bbc565b6060836001600160a01b031683604051613f3f9190614563565b6000604051808303816000865af19150503d8060008114613f7c576040519150601f19603f3d011682016040523d82523d6000602084013e613f81565b606091505b50909250905081801561133b57508051158061133b57508080602001905181019061133b91906143ba565b6000806000613fbb868661413b565b90506000613fe3613fd483670de0b6b3a764000061471b565b86670de0b6b3a7640000614156565b9050670de0b6b3a764000081116140165784613fff898b614774565b6140099190614733565b6000935093505050614095565b600061402a670de0b6b3a764000083614774565b838a670de0b6b3a764000061403f868f614755565b6140499190614733565b6140539190614774565b61405d9190614755565b6140679190614733565b90506140748a89896141b8565b93508381101561408557600061408f565b61408f8482614774565b94505050505b9550959350505050565b60008387116140b057506000614131565b6000620151806140c0868a614774565b6140cd906201517f61471b565b6140d79190614733565b6140e49062015180614755565b90506140fa876140f4858961471b565b836141b8565b614104908361471b565b9150670de0b6b3a76400006141198886614755565b6141239190614733565b61412d908361471b565b9150505b9695505050505050565b60006301e1338061414c8385614755565b6139c19190614733565b6000600183166141665781614168565b835b90505b60019290921c9182156139c157816141838580614755565b61418d9190614733565b93506001831661419c5761416b565b816141a78583614755565b6141b19190614733565b905061416b565b6000670de0b6b3a76400006141cd848461413b565b6141d79086614755565b6139be9190614733565b60405180606001604052806003906020820280368337509192915050565b60405180604001604052806002906020820280368337509192915050565b60008083601f84011261422f57600080fd5b50813567ffffffffffffffff81111561424757600080fd5b60208301915083602082850101111561425f57600080fd5b9250929050565b60006020828403121561427857600080fd5b81356139c1816147b7565b60006020828403121561429557600080fd5b81516139c1816147b7565b600080604083850312156142b357600080fd5b82356142be816147b7565b915060208301356142ce816147b7565b809150509250929050565b6000806000604084860312156142ee57600080fd5b83356142f9816147b7565b9250602084013567ffffffffffffffff81111561431557600080fd5b6143218682870161421d565b9497909650939450505050565b6000806000806060858703121561434457600080fd5b843561434f816147b7565b935060208501359250604085013567ffffffffffffffff8082111561437357600080fd5b818701915087601f83011261438757600080fd5b81358181111561439657600080fd5b8860208260051b85010111156143ab57600080fd5b95989497505060200194505050565b6000602082840312156143cc57600080fd5b815180151581146139c157600080fd5b6000602082840312156143ee57600080fd5b5035919050565b60006020828403121561440757600080fd5b5051919050565b6000806040838503121561442157600080fd5b8235915060208301356142ce816147b7565b60008060006040848603121561444857600080fd5b83359250602084013567ffffffffffffffff81111561431557600080fd5b6000806000806080858703121561447c57600080fd5b505082516020840151604085015160609095015191969095509092509050565b818352600060208085019450848460051b86018460005b8781101561451d5783830389528135601e198836030181126144d457600080fd5b8701803567ffffffffffffffff8111156144ed57600080fd5b8036038913156144fc57600080fd5b614509858289850161452a565b9a87019a94505050908401906001016144b3565b5090979650505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8183823760009101908152919050565b6000825160005b81811015614584576020818601810151858301520161456a565b81811115614593576000828501525b509190910192915050565b60018060a01b038516815283602082015260606040820152600061413160608301848661449c565b85815260018060a01b03851660208201528360408201526080606082015260006145f460808301848661449c565b979650505050505050565b60208082526015908201527413530e93540e9514905394d1915497d19052531151605a1b604082015260600190565b602080825260119082015270130e941493d513d0d3d317d4105554d151607a1b604082015260600190565b83815260c0810160208083018560005b600381101561468657815183529183019190830190600101614669565b505050608083018460005b60028110156146ae57815183529183019190830190600101614691565b50505050949350505050565b83815260406020820152600061394960408301848661452a565b6000808335601e198436030181126146eb57600080fd5b83018035915067ffffffffffffffff82111561470657600080fd5b60200191503681900382131561425f57600080fd5b6000821982111561472e5761472e61478b565b500190565b60008261475057634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561476f5761476f61478b565b500290565b6000828210156147865761478661478b565b500390565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6001600160a01b03811681146133c857600080fdfe6bd56533ce1c8ea03f7b858ac441b5a86d140a793a7c9e3faecbbe517c2c8791a26469706673582212204a309c37cbae06ae4046df36b05aa1ac996ec534c14d5709848916960b7ff5da64736f6c63430008070033

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.