Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
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
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; import { IERC20 } from "../modules/erc20/contracts/interfaces/IERC20.sol"; import { ERC20Helper } from "../modules/erc20-helper/src/ERC20Helper.sol"; import { IMapleProxyFactory } from "../modules/maple-proxy-factory/contracts/interfaces/IMapleProxyFactory.sol"; import { IMapleLoan } from "./interfaces/IMapleLoan.sol"; import { IMapleGlobalsLike } from "./interfaces/Interfaces.sol"; import { MapleLoanInternals } from "./MapleLoanInternals.sol"; /// @title MapleLoan implements a primitive loan with additional functionality, and is intended to be proxied. contract MapleLoan is IMapleLoan, MapleLoanInternals { modifier whenProtocolNotPaused() { require(!isProtocolPaused(), "ML:PROTOCOL_PAUSED"); _; } /********************************/ /*** Administrative Functions ***/ /********************************/ function migrate(address migrator_, bytes calldata arguments_) external override { require(msg.sender == _factory(), "ML:M:NOT_FACTORY"); require(_migrate(migrator_, arguments_), "ML:M:FAILED"); } function setImplementation(address newImplementation_) external override { require(msg.sender == _factory(), "ML:SI:NOT_FACTORY"); require(_setImplementation(newImplementation_), "ML:SI:FAILED"); } function upgrade(uint256 toVersion_, bytes calldata arguments_) external override { require(msg.sender == IMapleGlobalsLike(_mapleGlobals()).globalAdmin(), "ML:U:NOT_ADMIN"); emit Upgraded(toVersion_, arguments_); IMapleProxyFactory(_factory()).upgradeInstance(toVersion_, arguments_); } /************************/ /*** Borrow Functions ***/ /************************/ function acceptBorrower() external override { require(msg.sender == _pendingBorrower, "ML:AB:NOT_PENDING_BORROWER"); _pendingBorrower = address(0); emit BorrowerAccepted(_borrower = msg.sender); } function closeLoan(uint256 amount_) external override returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) { uint256 drawableFundsBeforePayment = _drawableFunds; // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs. require(amount_ == uint256(0) || ERC20Helper.transferFrom(_fundsAsset, msg.sender, address(this), amount_), "ML:CL:TRANSFER_FROM_FAILED"); ( principal_, interest_, delegateFee_, treasuryFee_ ) = _closeLoan(); // Either the caller is the borrower or `_drawableFunds` has not decreased. require(msg.sender == _borrower || _drawableFunds >= drawableFundsBeforePayment, "ML:CL:CANNOT_USE_DRAWABLE"); emit LoanClosed(principal_, interest_, delegateFee_, treasuryFee_); } function drawdownFunds(uint256 amount_, address destination_) external override whenProtocolNotPaused returns (uint256 collateralPosted_) { require(msg.sender == _borrower, "ML:DF:NOT_BORROWER"); emit FundsDrawnDown(amount_, destination_); // Post additional collateral required to facilitate this drawdown, if needed. uint256 additionalCollateralRequired = getAdditionalCollateralRequiredFor(amount_); if (additionalCollateralRequired > uint256(0)) { // Determine collateral currently unaccounted for. uint256 unaccountedCollateral = _getUnaccountedAmount(_collateralAsset); // Post required collateral, specifying then amount lacking as the optional amount to be transferred from. collateralPosted_ = postCollateral( additionalCollateralRequired > unaccountedCollateral ? additionalCollateralRequired - unaccountedCollateral : uint256(0) ); } _drawdownFunds(amount_, destination_); } function makePayment(uint256 amount_) external override returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) { uint256 drawableFundsBeforePayment = _drawableFunds; // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs. require(amount_ == uint256(0) || ERC20Helper.transferFrom(_fundsAsset, msg.sender, address(this), amount_), "ML:MP:TRANSFER_FROM_FAILED"); ( principal_, interest_, delegateFee_, treasuryFee_ ) = _makePayment(); // Either the caller is the borrower or `_drawableFunds` has not decreased. require(msg.sender == _borrower || _drawableFunds >= drawableFundsBeforePayment, "ML:MP:CANNOT_USE_DRAWABLE"); emit PaymentMade(principal_, interest_, delegateFee_, treasuryFee_); } function postCollateral(uint256 amount_) public override whenProtocolNotPaused returns (uint256 collateralPosted_) { // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs. require( amount_ == uint256(0) || ERC20Helper.transferFrom(_collateralAsset, msg.sender, address(this), amount_), "ML:PC:TRANSFER_FROM_FAILED" ); emit CollateralPosted(collateralPosted_ = _postCollateral()); } function proposeNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external override whenProtocolNotPaused { require(msg.sender == _borrower, "ML:PNT:NOT_BORROWER"); require(deadline_ >= block.timestamp, "ML:PNT:INVALID_DEADLINE"); emit NewTermsProposed(_proposeNewTerms(refinancer_, deadline_, calls_), refinancer_, deadline_, calls_); } function removeCollateral(uint256 amount_, address destination_) external override whenProtocolNotPaused { require(msg.sender == _borrower, "ML:RC:NOT_BORROWER"); emit CollateralRemoved(amount_, destination_); _removeCollateral(amount_, destination_); } function returnFunds(uint256 amount_) external override whenProtocolNotPaused returns (uint256 fundsReturned_) { // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs. require(amount_ == uint256(0) || ERC20Helper.transferFrom(_fundsAsset, msg.sender, address(this), amount_), "ML:RF:TRANSFER_FROM_FAILED"); emit FundsReturned(fundsReturned_ = _returnFunds()); } function setPendingBorrower(address pendingBorrower_) external override { require(msg.sender == _borrower, "ML:SPB:NOT_BORROWER"); emit PendingBorrowerSet(_pendingBorrower = pendingBorrower_); } /**********************/ /*** Lend Functions ***/ /**********************/ function acceptLender() external override { require(msg.sender == _pendingLender, "ML:AL:NOT_PENDING_LENDER"); _pendingLender = address(0); emit LenderAccepted(_lender = msg.sender); } function acceptNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_, uint256 amount_) external override whenProtocolNotPaused { address lenderAddress = _lender; require(msg.sender == lenderAddress, "ML:ANT:NOT_LENDER"); address fundsAssetAddress = _fundsAsset; // The amount specified is an optional amount to be transfer from the caller, as a convenience for EOAs. require(amount_ == uint256(0) || ERC20Helper.transferFrom(fundsAssetAddress, msg.sender, address(this), amount_), "ML:ANT:TRANSFER_FROM_FAILED"); emit NewTermsAccepted(_acceptNewTerms(refinancer_, deadline_, calls_), refinancer_, deadline_, calls_); emit EstablishmentFeesSet(_delegateFee, _treasuryFee); uint256 extra = _getUnaccountedAmount(fundsAssetAddress); // NOTE: This block ensures unaccounted funds (pre-existing or due to over-funding) gets redirected to the lender. if (extra > uint256(0)) { emit FundsRedirected(extra, lenderAddress); require(ERC20Helper.transfer(fundsAssetAddress, lenderAddress, extra), "ML:ANT:TRANSFER_FAILED"); } } function claimFunds(uint256 amount_, address destination_) external override whenProtocolNotPaused { require(msg.sender == _lender, "ML:CF:NOT_LENDER"); emit FundsClaimed(amount_, destination_); _claimFunds(amount_, destination_); } function fundLoan(address lender_, uint256 amount_) external override whenProtocolNotPaused returns (uint256 fundsLent_) { address fundsAssetAddress = _fundsAsset; // The amount specified is an optional amount to be transferred from the caller, as a convenience for EOAs. require(amount_ == uint256(0) || ERC20Helper.transferFrom(fundsAssetAddress, msg.sender, address(this), amount_), "ML:FL:TRANSFER_FROM_FAILED"); // If the loan is not active, fund it. if (_nextPaymentDueDate == uint256(0)) { // NOTE: `_nextPaymentDueDate` emitted in event is updated by `_fundLoan`. emit Funded(lender_, fundsLent_ = _fundLoan(lender_), _nextPaymentDueDate); } uint256 extra = _getUnaccountedAmount(fundsAssetAddress); address lenderAddress = _lender; // NOTE: This block is not only a stopgap solution to allow a LiquidityLockerV1 to send funds to a DebtLocker, while maintaining PoolV1 accounting, // but also ensures unaccounted funds (pre-existing or due to over-funding) gets redirected to the lender. if (extra > uint256(0)) { emit FundsRedirected(extra, lenderAddress); require(ERC20Helper.transfer(fundsAssetAddress, lenderAddress, extra), "ML:FL:TRANSFER_FAILED"); } } function repossess(address destination_) external override whenProtocolNotPaused returns (uint256 collateralRepossessed_, uint256 fundsRepossessed_) { require(msg.sender == _lender, "ML:R:NOT_LENDER"); ( collateralRepossessed_, fundsRepossessed_ ) = _repossess(destination_); emit Repossessed(collateralRepossessed_, fundsRepossessed_, destination_); } function setPendingLender(address pendingLender_) external override { require(msg.sender == _lender, "ML:SPL:NOT_LENDER"); emit PendingLenderSet(_pendingLender = pendingLender_); } /*******************************/ /*** Miscellaneous Functions ***/ /*******************************/ function rejectNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external override whenProtocolNotPaused { require((msg.sender == _borrower) || (msg.sender == _lender), "L:RNT:NO_AUTH"); emit NewTermsRejected(_rejectNewTerms(refinancer_, deadline_, calls_), refinancer_, deadline_, calls_); } function skim(address token_, address destination_) external override whenProtocolNotPaused returns (uint256 skimmed_) { require((msg.sender == _borrower) || (msg.sender == _lender), "L:S:NO_AUTH"); require((token_ != _fundsAsset) && (token_ != _collateralAsset), "L:S:INVALID_TOKEN"); emit Skimmed(token_, skimmed_ = IERC20(token_).balanceOf(address(this)), destination_); require(ERC20Helper.transfer(token_, destination_, skimmed_), "L:S:TRANSFER_FAILED"); } /**********************/ /*** View Functions ***/ /**********************/ function getAdditionalCollateralRequiredFor(uint256 drawdown_) public view override returns (uint256 collateral_) { // Determine the collateral needed in the contract for a reduced drawable funds amount. uint256 collateralNeeded = _getCollateralRequiredFor(_principal, _drawableFunds - drawdown_, _principalRequested, _collateralRequired); uint256 currentCollateral = _collateral; return collateralNeeded > currentCollateral ? collateralNeeded - currentCollateral : uint256(0); } function getEarlyPaymentBreakdown() external view override returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) { ( principal_, interest_, delegateFee_, treasuryFee_ ) = _getEarlyPaymentBreakdown(); } function getNextPaymentBreakdown() external view override returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) { ( principal_, interest_, delegateFee_, treasuryFee_ ) = _getNextPaymentBreakdown(); } function getRefinanceInterest(uint256 timestamp_) external view override returns (uint256 proRataInterest_) { ( proRataInterest_, ) = _getRefinanceInterestParams( timestamp_, _paymentInterval, _principal, _endingPrincipal, _interestRate, _paymentsRemaining, _nextPaymentDueDate, _lateFeeRate, _lateInterestPremium ); } function isProtocolPaused() public view override returns (bool paused_) { return IMapleGlobalsLike(IMapleProxyFactory(_factory()).mapleGlobals()).protocolPaused(); } /****************************/ /*** State View Functions ***/ /****************************/ function borrower() external view override returns (address borrower_) { return _borrower; } function claimableFunds() external view override returns (uint256 claimableFunds_) { return _claimableFunds; } function collateral() external view override returns (uint256 collateral_) { return _collateral; } function collateralAsset() external view override returns (address collateralAsset_) { return _collateralAsset; } function collateralRequired() external view override returns (uint256 collateralRequired_) { return _collateralRequired; } function delegateFee() external view override returns (uint256 delegateFee_) { return _delegateFee; } function drawableFunds() external view override returns (uint256 drawableFunds_) { return _drawableFunds; } function earlyFeeRate() external view override returns (uint256 earlyFeeRate_) { return _earlyFeeRate; } function endingPrincipal() external view override returns (uint256 endingPrincipal_) { return _endingPrincipal; } function excessCollateral() external view override returns (uint256 excessCollateral_) { uint256 collateralNeeded = _getCollateralRequiredFor(_principal, _drawableFunds, _principalRequested, _collateralRequired); uint256 currentCollateral = _collateral; return currentCollateral > collateralNeeded ? currentCollateral - collateralNeeded : uint256(0); } function factory() external view override returns (address factory_) { return _factory(); } function fundsAsset() external view override returns (address fundsAsset_) { return _fundsAsset; } function gracePeriod() external view override returns (uint256 gracePeriod_) { return _gracePeriod; } function implementation() external view override returns (address implementation_) { return _implementation(); } function interestRate() external view override returns (uint256 interestRate_) { return _interestRate; } function lateFeeRate() external view override returns (uint256 lateFeeRate_) { return _lateFeeRate; } function lateInterestPremium() external view override returns (uint256 lateInterestPremium_) { return _lateInterestPremium; } function lender() external view override returns (address lender_) { return _lender; } function nextPaymentDueDate() external view override returns (uint256 nextPaymentDueDate_) { return _nextPaymentDueDate; } function paymentInterval() external view override returns (uint256 paymentInterval_) { return _paymentInterval; } function paymentsRemaining() external view override returns (uint256 paymentsRemaining_) { return _paymentsRemaining; } function pendingBorrower() external view override returns (address pendingBorrower_) { return _pendingBorrower; } function pendingLender() external view override returns (address pendingLender_) { return _pendingLender; } function principalRequested() external view override returns (uint256 principalRequested_) { return _principalRequested; } function principal() external view override returns (uint256 principal_) { return _principal; } function refinanceCommitment() external view override returns (bytes32 refinanceCommitment_) { return _refinanceCommitment; } function refinanceInterest() external view override returns (uint256 refinanceInterest_) { return _refinanceInterest; } // NOTE: This is needed for `fundLoan` call from PoolV1. function superFactory() external view override returns (address superFactory_) { return _factory(); } function treasuryFee() external view override returns (uint256 treasuryFee_) { return _treasuryFee; } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; import { IERC20 } from "../modules/erc20/contracts/interfaces/IERC20.sol"; import { ERC20Helper } from "../modules/erc20-helper/src/ERC20Helper.sol"; import { MapleProxiedInternals } from "../modules/maple-proxy-factory/contracts/MapleProxiedInternals.sol"; import { ILenderLike, IMapleGlobalsLike } from "./interfaces/Interfaces.sol"; import { IMapleLoanFactory } from "./interfaces/IMapleLoanFactory.sol"; /// @title MapleLoanInternals defines the storage layout and internal logic of MapleLoan. abstract contract MapleLoanInternals is MapleProxiedInternals { uint256 private constant SCALED_ONE = uint256(10 ** 18); // Roles address internal _borrower; // The address of the borrower. address internal _lender; // The address of the lender. address internal _pendingBorrower; // The address of the pendingBorrower, the only address that can accept the borrower role. address internal _pendingLender; // The address of the pendingLender, the only address that can accept the lender role. // Assets address internal _collateralAsset; // The address of the asset used as collateral. address internal _fundsAsset; // The address of the asset used as funds. // Loan Term Parameters uint256 internal _gracePeriod; // The number of seconds a payment can be late. uint256 internal _paymentInterval; // The number of seconds between payments. // Rates uint256 internal _interestRate; // The annualized interest rate of the loan. uint256 internal _earlyFeeRate; // The fee rate for prematurely closing loans. uint256 internal _lateFeeRate; // The fee rate for late payments. uint256 internal _lateInterestPremium; // The amount to increase the interest rate by for late payments. // Requested Amounts uint256 internal _collateralRequired; // The collateral the borrower is expected to put up to draw down all _principalRequested. uint256 internal _principalRequested; // The funds the borrowers wants to borrow. uint256 internal _endingPrincipal; // The principal to remain at end of loan. // State uint256 internal _drawableFunds; // The amount of funds that can be drawn down. uint256 internal _claimableFunds; // The amount of funds that the lender can claim (principal repayments, interest, etc). uint256 internal _collateral; // The amount of collateral, in collateral asset, that is currently posted. uint256 internal _nextPaymentDueDate; // The timestamp of due date of next payment. uint256 internal _paymentsRemaining; // The number of payments remaining. uint256 internal _principal; // The amount of principal yet to be paid down. // Refinance bytes32 internal _refinanceCommitment; uint256 internal _refinanceInterest; // Establishment fees uint256 internal _delegateFee; uint256 internal _treasuryFee; /**********************************/ /*** Internal General Functions ***/ /**********************************/ /// @dev Clears all state variables to end a loan, but keep borrower and lender withdrawal functionality intact. function _clearLoanAccounting() internal { _gracePeriod = uint256(0); _paymentInterval = uint256(0); _interestRate = uint256(0); _earlyFeeRate = uint256(0); _lateFeeRate = uint256(0); _lateInterestPremium = uint256(0); _endingPrincipal = uint256(0); _nextPaymentDueDate = uint256(0); _paymentsRemaining = uint256(0); _principal = uint256(0); _delegateFee = uint256(0); _treasuryFee = uint256(0); } /** * @dev Initializes the loan. * @param borrower_ The address of the borrower. * @param assets_ Array of asset addresses. * [0]: collateralAsset, * [1]: fundsAsset. * @param termDetails_ Array of loan parameters: * [0]: gracePeriod, * [1]: paymentInterval, * [2]: payments, * @param amounts_ Requested amounts: * [0]: collateralRequired, * [1]: principalRequested, * [2]: endingPrincipal. * @param rates_ Fee parameters: * [0]: interestRate, * [1]: earlyFeeRate, * [2]: lateFeeRate, * [3]: lateInterestPremium. */ function _initialize( address borrower_, address[2] memory assets_, uint256[3] memory termDetails_, uint256[3] memory amounts_, uint256[4] memory rates_ ) internal { // Principal requested needs to be non-zero (see `_getCollateralRequiredFor` math). require(amounts_[1] > uint256(0), "MLI:I:INVALID_PRINCIPAL"); // Ending principal needs to be less than or equal to principal requested. require(amounts_[2] <= amounts_[1], "MLI:I:INVALID_ENDING_PRINCIPAL"); require((_borrower = borrower_) != address(0), "MLI:I:INVALID_BORROWER"); _collateralAsset = assets_[0]; _fundsAsset = assets_[1]; _gracePeriod = termDetails_[0]; _paymentInterval = termDetails_[1]; _paymentsRemaining = termDetails_[2]; _collateralRequired = amounts_[0]; _principalRequested = amounts_[1]; _endingPrincipal = amounts_[2]; _interestRate = rates_[0]; _earlyFeeRate = rates_[1]; _lateFeeRate = rates_[2]; _lateInterestPremium = rates_[3]; _setEstablishmentFees(amounts_[1], termDetails_[1], uint256(0), uint256(0)); } /**************************************/ /*** Internal Borrow-side Functions ***/ /**************************************/ /// @dev Prematurely ends a loan by making all remaining payments. function _closeLoan() internal returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) { require(block.timestamp <= _nextPaymentDueDate, "MLI:CL:PAYMENT_IS_LATE"); ( principal_, interest_, delegateFee_, treasuryFee_ ) = _getEarlyPaymentBreakdown(); _refinanceInterest = uint256(0); uint256 principalAndInterest = principal_ + interest_; // The drawable funds are increased by the extra funds in the contract, minus the total needed for payment. // NOTE: This line will revert if not enough funds were added for the full payment amount. _drawableFunds = (_drawableFunds + _getUnaccountedAmount(_fundsAsset)) - (principalAndInterest + delegateFee_ + treasuryFee_); _claimableFunds += principalAndInterest; _processEstablishmentFees(delegateFee_, treasuryFee_); _clearLoanAccounting(); } /// @dev Sends `amount_` of `_drawableFunds` to `destination_`. function _drawdownFunds(uint256 amount_, address destination_) internal { _drawableFunds -= amount_; require(ERC20Helper.transfer(_fundsAsset, destination_, amount_), "MLI:DF:TRANSFER_FAILED"); require(_isCollateralMaintained(), "MLI:DF:INSUFFICIENT_COLLATERAL"); } /// @dev Makes a payment to progress the loan closer to maturity. function _makePayment() internal returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) { ( principal_, interest_, delegateFee_, treasuryFee_ ) = _getNextPaymentBreakdown(); _refinanceInterest = uint256(0); uint256 principalAndInterest = principal_ + interest_; // The drawable funds are increased by the extra funds in the contract, minus the total needed for payment. // NOTE: This line will revert if not enough funds were added for the full payment amount. _drawableFunds = (_drawableFunds + _getUnaccountedAmount(_fundsAsset)) - (principalAndInterest + delegateFee_ + treasuryFee_); _claimableFunds += principalAndInterest; _processEstablishmentFees(delegateFee_, treasuryFee_); uint256 paymentsRemaining = _paymentsRemaining; if (paymentsRemaining == uint256(1)) { _clearLoanAccounting(); // Assumes `_getNextPaymentBreakdown` returns a `principal_` that is `_principal`. } else { _nextPaymentDueDate += _paymentInterval; _principal -= principal_; _paymentsRemaining = paymentsRemaining - uint256(1); } } /// @dev Registers the delivery of an amount of collateral to be posted. function _postCollateral() internal returns (uint256 collateralPosted_) { _collateral += (collateralPosted_ = _getUnaccountedAmount(_collateralAsset)); } /// @dev Sets refinance commitment given refinance operations. function _proposeNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) internal returns (bytes32 proposedRefinanceCommitment_) { return _refinanceCommitment = calls_.length > uint256(0) ? _getRefinanceCommitment(refinancer_, deadline_, calls_) : bytes32(0); } /// @dev Sends `amount_` of `_collateral` to `destination_`. function _removeCollateral(uint256 amount_, address destination_) internal { _collateral -= amount_; require(ERC20Helper.transfer(_collateralAsset, destination_, amount_), "MLI:RC:TRANSFER_FAILED"); require(_isCollateralMaintained(), "MLI:RC:INSUFFICIENT_COLLATERAL"); } /// @dev Registers the delivery of an amount of funds to be returned as `_drawableFunds`. function _returnFunds() internal returns (uint256 fundsReturned_) { _drawableFunds += (fundsReturned_ = _getUnaccountedAmount(_fundsAsset)); } /************************************/ /*** Internal Lend-side Functions ***/ /************************************/ /// @dev Processes refinance operations. function _acceptNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) internal returns (bytes32 acceptedRefinanceCommitment_) { // NOTE: A zero refinancer address and/or empty calls array will never (probabilistically) match a refinance commitment in storage. require(_refinanceCommitment == (acceptedRefinanceCommitment_ = _getRefinanceCommitment(refinancer_, deadline_, calls_)), "MLI:ANT:COMMITMENT_MISMATCH"); require(refinancer_.code.length != uint256(0), "MLI:ANT:INVALID_REFINANCER"); require(block.timestamp <= deadline_, "MLI:ANT:EXPIRED_COMMITMENT"); uint256 preRefinancePaymentInterval = _paymentInterval; // Get the amount of interest owed since the last payment due date, as well as the time since the last due date ( uint256 proRataInterest, uint256 timeSinceLastPaymentDueDate ) = _getRefinanceInterestParams( block.timestamp, preRefinancePaymentInterval, _principal, _endingPrincipal, _interestRate, _paymentsRemaining, _nextPaymentDueDate, _lateFeeRate, _lateInterestPremium ); // In case there is still a refinance interest, just increment it instead of setting it. _refinanceInterest += proRataInterest; // Clear refinance commitment to prevent implications of re-acceptance of another call to `_acceptNewTerms`. _refinanceCommitment = bytes32(0); uint256 length = calls_.length; for (uint256 i; i < length;) { ( bool success, ) = refinancer_.delegatecall(calls_[i]); require(success, "MLI:ANT:FAILED"); unchecked { ++i; } } // Track any uncaptured establishment fees and spread over the course of new loan. // If `timeSinceLastPaymentDueDate` is zero, these will both equal zero. uint256 extraDelegateFee = (_delegateFee * timeSinceLastPaymentDueDate) / (preRefinancePaymentInterval * _paymentsRemaining); uint256 extraTreasuryFee = (_treasuryFee * timeSinceLastPaymentDueDate) / (preRefinancePaymentInterval * _paymentsRemaining); // Increment the due date to be one full payment interval from now, to restart the payment schedule with new terms. // NOTE: `_paymentInterval` here is possibly newly set via the above delegate calls, so cache it. uint256 paymentInterval = _paymentInterval; _nextPaymentDueDate = block.timestamp + paymentInterval; // Update establishment fees. // NOTE: `_principal` here is possibly newly increased/decreased via the above delegate calls. _setEstablishmentFees(_principal, paymentInterval, extraDelegateFee, extraTreasuryFee); // Ensure that collateral is maintained after changes made. require(_isCollateralMaintained(), "MLI:ANT:INSUFFICIENT_COLLATERAL"); } /// @dev Sends `amount_` of `_claimableFunds` to `destination_`. /// If `amount_` is higher than `_claimableFunds` the transaction will underflow and revert. function _claimFunds(uint256 amount_, address destination_) internal { _claimableFunds -= amount_; require(ERC20Helper.transfer(_fundsAsset, destination_, amount_), "MLI:CF:TRANSFER_FAILED"); } /// @dev Fund the loan and kick off the repayment requirements. function _fundLoan(address lender_) internal returns (uint256 fundsLent_) { require(lender_ != address(0), "MLI:FL:INVALID_LENDER"); uint256 paymentsRemaining = _paymentsRemaining; // Can only fund loan if there are payments remaining (as defined by the initialization) and no payment is due yet (as set by a funding). require((_nextPaymentDueDate == uint256(0)) && (paymentsRemaining != uint256(0)), "MLI:FL:LOAN_ACTIVE"); uint256 paymentInterval = _paymentInterval; _lender = lender_; _nextPaymentDueDate = block.timestamp + paymentInterval; // Amount funded and principal are as requested. fundsLent_ = _principal = _principalRequested; address fundsAsset = _fundsAsset; // Cannot under-fund loan, but over-funding results in additional funds left unaccounted for. require(_getUnaccountedAmount(fundsAsset) >= fundsLent_, "MLI:FL:WRONG_FUND_AMOUNT"); _drawableFunds = fundsLent_; } /// @dev Process establishment fees for a payment. function _processEstablishmentFees(uint256 delegateFee_, uint256 treasuryFee_) internal { if (!_sendFee(_lender, ILenderLike.poolDelegate.selector, delegateFee_)) { _claimableFunds += delegateFee_; } if (!_sendFee(_mapleGlobals(), IMapleGlobalsLike.mapleTreasury.selector, treasuryFee_)) { _claimableFunds += treasuryFee_; } } /// @dev Explicitly cancel a proposed refinance commitment function _rejectNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) internal returns (bytes32 rejectedRefinanceCommitment_){ require(_refinanceCommitment == (rejectedRefinanceCommitment_ = _getRefinanceCommitment(refinancer_, deadline_, calls_)), "MLI:RNT:COMMITMENT_MISMATCH"); _refinanceCommitment = bytes32(0); } function _sendFee(address lookup_, bytes4 selector_, uint256 amount_) internal returns (bool success_) { if (amount_ == uint256(0)) return true; ( bool success , bytes memory data ) = lookup_.call(abi.encodeWithSelector(selector_)); if (!success || data.length != uint256(32)) return false; address destination = abi.decode(data, (address)); if (destination == address(0)) return false; return ERC20Helper.transfer(_fundsAsset, destination, amount_); } /// @dev Set establishment fees for funds lent, capturing unpaid establishment fees from refinance. function _setEstablishmentFees(uint256 fundsLent_, uint256 paymentInterval_, uint256 extraDelegateFee_, uint256 extraTreasuryFee_) internal { IMapleGlobalsLike globals = IMapleGlobalsLike(_mapleGlobals()); // Store the annualized delegate fee. _delegateFee = (fundsLent_ * globals.investorFee() * paymentInterval_) / uint256(365 days * 10_000) + extraDelegateFee_; // Store the annualized treasury fee. _treasuryFee = (fundsLent_ * globals.treasuryFee() * paymentInterval_) / uint256(365 days * 10_000) + extraTreasuryFee_; } /// @dev Reset all state variables in order to release funds and collateral of a loan in default. function _repossess(address destination_) internal returns (uint256 collateralRepossessed_, uint256 fundsRepossessed_) { uint256 nextPaymentDueDate = _nextPaymentDueDate; require( nextPaymentDueDate != uint256(0) && (block.timestamp > nextPaymentDueDate + _gracePeriod), "MLI:R:NOT_IN_DEFAULT" ); _clearLoanAccounting(); // Uniquely in `_repossess`, stop accounting for all funds so that they can be swept. _collateral = uint256(0); _claimableFunds = uint256(0); _drawableFunds = uint256(0); address collateralAsset = _collateralAsset; // Either there is no collateral to repossess, or the transfer of the collateral succeeds. require( (collateralRepossessed_ = _getUnaccountedAmount(collateralAsset)) == uint256(0) || ERC20Helper.transfer(collateralAsset, destination_, collateralRepossessed_), "MLI:R:C_TRANSFER_FAILED" ); address fundsAsset = _fundsAsset; // Either there are no funds to repossess, or the transfer of the funds succeeds. require( (fundsRepossessed_ = _getUnaccountedAmount(fundsAsset)) == uint256(0) || ERC20Helper.transfer(fundsAsset, destination_, fundsRepossessed_), "MLI:R:F_TRANSFER_FAILED" ); } /*******************************/ /*** Internal View Functions ***/ /*******************************/ /// @dev Returns whether the amount of collateral posted is commensurate with the amount of drawn down (outstanding) principal. function _isCollateralMaintained() internal view returns (bool isMaintained_) { return _collateral >= _getCollateralRequiredFor(_principal, _drawableFunds, _principalRequested, _collateralRequired); } /// @dev Get principal and interest breakdown for paying off the entire loan early. function _getEarlyPaymentBreakdown() internal view returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) { // Compute interest and include any uncaptured interest from refinance. interest_ = (((principal_ = _principal) * _earlyFeeRate) / SCALED_ONE) + _refinanceInterest; uint256 paymentsRemaining = _paymentsRemaining; delegateFee_ = _delegateFee * paymentsRemaining; treasuryFee_ = _treasuryFee * paymentsRemaining; } /// @dev Get principal and interest breakdown for next standard payment. function _getNextPaymentBreakdown() internal view returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_) { ( principal_, interest_ ) = _getPaymentBreakdown( block.timestamp, _nextPaymentDueDate, _paymentInterval, _principal, _endingPrincipal, _paymentsRemaining, _interestRate, _lateFeeRate, _lateInterestPremium ); // Include any uncaptured interest from refinance. interest_ += _refinanceInterest; delegateFee_ = _delegateFee; treasuryFee_ = _treasuryFee; } /// @dev Returns the amount of an `asset_` that this contract owns, which is not currently accounted for by its state variables. function _getUnaccountedAmount(address asset_) internal view returns (uint256 unaccountedAmount_) { return IERC20(asset_).balanceOf(address(this)) - (asset_ == _collateralAsset ? _collateral : uint256(0)) // `_collateral` is `_collateralAsset` accounted for. - (asset_ == _fundsAsset ? _claimableFunds + _drawableFunds : uint256(0)); // `_claimableFunds` and `_drawableFunds` are `_fundsAsset` accounted for. } /// @dev Returns the address of the Maple Globals contract. function _mapleGlobals() internal view returns (address mapleGlobals_) { return IMapleLoanFactory(_factory()).mapleGlobals(); } /*******************************/ /*** Internal Pure Functions ***/ /*******************************/ /// @dev Returns the total collateral to be posted for some drawn down (outstanding) principal and overall collateral ratio requirement. function _getCollateralRequiredFor( uint256 principal_, uint256 drawableFunds_, uint256 principalRequested_, uint256 collateralRequired_ ) internal pure returns (uint256 collateral_) { // Where (collateral / outstandingPrincipal) should be greater or equal to (collateralRequired / principalRequested). // NOTE: principalRequested_ cannot be 0, which is reasonable, since it means this was never a loan. return principal_ <= drawableFunds_ ? uint256(0) : (collateralRequired_ * (principal_ - drawableFunds_)) / principalRequested_; } /// @dev Returns principal and interest portions of a payment instalment, given generic, stateless loan parameters. function _getInstallment(uint256 principal_, uint256 endingPrincipal_, uint256 interestRate_, uint256 paymentInterval_, uint256 totalPayments_) internal pure returns (uint256 principalAmount_, uint256 interestAmount_) { /*************************************************************************************************\ * | * * A = installment amount | / \ / R \ * * P = principal remaining | | / \ | | ----------------------- | * * R = interest rate | A = | | P * ( 1 + R ) ^ N | - E | * | / \ | * * N = payments remaining | | \ / | | | ( 1 + R ) ^ N | - 1 | * * E = ending principal target | \ / \ \ / / * * | * * |---------------------------------------------------------------- * * * * - Where R is `periodicRate` * * - Where (1 + R) ^ N is `raisedRate` * * - Both of these rates are scaled by 1e18 (e.g., 12% => 0.12 * 10 ** 18) * \*************************************************************************************************/ uint256 periodicRate = _getPeriodicInterestRate(interestRate_, paymentInterval_); uint256 raisedRate = _scaledExponent(SCALED_ONE + periodicRate, totalPayments_, SCALED_ONE); // NOTE: If a lack of precision in `_scaledExponent` results in a `raisedRate` smaller than one, assume it to be one and simplify the equation. if (raisedRate <= SCALED_ONE) return ((principal_ - endingPrincipal_) / totalPayments_, uint256(0)); uint256 total = ((((principal_ * raisedRate) / SCALED_ONE) - endingPrincipal_) * periodicRate) / (raisedRate - SCALED_ONE); interestAmount_ = _getInterest(principal_, interestRate_, paymentInterval_); principalAmount_ = total >= interestAmount_ ? total - interestAmount_ : uint256(0); } /// @dev Returns an amount by applying an annualized and scaled interest rate, to a principal, over an interval of time. function _getInterest(uint256 principal_, uint256 interestRate_, uint256 interval_) internal pure returns (uint256 interest_) { return (principal_ * _getPeriodicInterestRate(interestRate_, interval_)) / SCALED_ONE; } /// @dev Returns total principal and interest portion of a number of payments, given generic, stateless loan parameters and loan state. function _getPaymentBreakdown( uint256 currentTime_, uint256 nextPaymentDueDate_, uint256 paymentInterval_, uint256 principal_, uint256 endingPrincipal_, uint256 paymentsRemaining_, uint256 interestRate_, uint256 lateFeeRate_, uint256 lateInterestPremium_ ) internal pure returns (uint256 principalAmount_, uint256 interestAmount_) { ( principalAmount_, interestAmount_ ) = _getInstallment( principal_, endingPrincipal_, interestRate_, paymentInterval_, paymentsRemaining_ ); principalAmount_ = paymentsRemaining_ == uint256(1) ? principal_ : principalAmount_; interestAmount_ += _getLateInterest( currentTime_, principal_, interestRate_, nextPaymentDueDate_, lateFeeRate_, lateInterestPremium_ ); } function _getRefinanceInterestParams( uint256 currentTime_, uint256 paymentInterval_, uint256 principal_, uint256 endingPrincipal_, uint256 interestRate_, uint256 paymentsRemaining_, uint256 nextPaymentDueDate_, uint256 lateFeeRate_, uint256 lateInterestPremium_ ) internal pure returns (uint256 refinanceInterest_, uint256 timeSinceLastPaymentDueDate_) { // If the user has made an early payment, there is no refinance interest owed. if (currentTime_ + paymentInterval_ < nextPaymentDueDate_) return (0, 0); timeSinceLastPaymentDueDate_ = currentTime_ - (nextPaymentDueDate_ - paymentInterval_); ( , refinanceInterest_ ) = _getInstallment( principal_, endingPrincipal_, interestRate_, timeSinceLastPaymentDueDate_, paymentsRemaining_ ); refinanceInterest_ += _getLateInterest( currentTime_, principal_, interestRate_, nextPaymentDueDate_, lateFeeRate_, lateInterestPremium_ ); } function _getLateInterest( uint256 currentTime_, uint256 principal_, uint256 interestRate_, uint256 nextPaymentDueDate_, uint256 lateFeeRate_, uint256 lateInterestPremium_ ) internal pure returns (uint256 lateInterest_) { if (currentTime_ <= nextPaymentDueDate_) return 0; // Calculates the number of full days late in seconds (will always be multiples of 86,400). // Rounds up and is inclusive so that if a payment is 1s late or 24h0m0s late it is 1 full day late. // 24h0m1s late would be two full days late. // (((86400n - 0n - 1n) / 86400n) + 1n) * 86400n = 86400n // (((86401n - 0n - 1n) / 86400n) + 1n) * 86400n = 172800n uint256 fullDaysLate = (((currentTime_ - nextPaymentDueDate_ - 1) / 1 days) + 1) * 1 days; lateInterest_ += _getInterest(principal_, interestRate_ + lateInterestPremium_, fullDaysLate); lateInterest_ += (lateFeeRate_ * principal_) / SCALED_ONE; } /// @dev Returns the interest rate over an interval, given an annualized interest rate. function _getPeriodicInterestRate(uint256 interestRate_, uint256 interval_) internal pure returns (uint256 periodicInterestRate_) { return (interestRate_ * interval_) / uint256(365 days); } /// @dev Returns refinance commitment given refinance parameters. function _getRefinanceCommitment(address refinancer_, uint256 deadline_, bytes[] calldata calls_) internal pure returns (bytes32 refinanceCommitment_) { return keccak256(abi.encode(refinancer_, deadline_, calls_)); } /** * @dev Returns exponentiation of a scaled base value. * * Walk through example: * LINE | base_ | exponent_ | one_ | result_ * | 3_00 | 18 | 1_00 | 0_00 * A | 3_00 | 18 | 1_00 | 1_00 * B | 3_00 | 9 | 1_00 | 1_00 * C | 9_00 | 9 | 1_00 | 1_00 * D | 9_00 | 9 | 1_00 | 9_00 * B | 9_00 | 4 | 1_00 | 9_00 * C | 81_00 | 4 | 1_00 | 9_00 * B | 81_00 | 2 | 1_00 | 9_00 * C | 6_561_00 | 2 | 1_00 | 9_00 * B | 6_561_00 | 1 | 1_00 | 9_00 * C | 43_046_721_00 | 1 | 1_00 | 9_00 * D | 43_046_721_00 | 1 | 1_00 | 387_420_489_00 * B | 43_046_721_00 | 0 | 1_00 | 387_420_489_00 * * Another implementation of this algorithm can be found in Dapphub's DSMath contract: * https://github.com/dapphub/ds-math/blob/ce67c0fa9f8262ecd3d76b9e4c026cda6045e96c/src/math.sol#L77 */ function _scaledExponent(uint256 base_, uint256 exponent_, uint256 one_) internal pure returns (uint256 result_) { // If exponent_ is odd, set result_ to base_, else set to one_. result_ = exponent_ & uint256(1) != uint256(0) ? base_ : one_; // A // Divide exponent_ by 2 (overwriting itself) and proceed if not zero. while ((exponent_ >>= uint256(1)) != uint256(0)) { // B base_ = (base_ * base_) / one_; // C // If exponent_ is even, go back to top. if (exponent_ & uint256(1) == uint256(0)) continue; // If exponent_ is odd, multiply result_ is multiplied by base_. result_ = (result_ * base_) / one_; // D } } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; import { IMapleProxied } from "../../modules/maple-proxy-factory/contracts/interfaces/IMapleProxied.sol"; import { IMapleLoanEvents } from "./IMapleLoanEvents.sol"; /// @title MapleLoan implements a primitive loan with additional functionality, and is intended to be proxied. interface IMapleLoan is IMapleProxied, IMapleLoanEvents { /***********************/ /*** State Variables ***/ /***********************/ /** * @dev The borrower of the loan, responsible for repayments. */ function borrower() external view returns (address borrower_); /** * @dev The amount of funds that have yet to be claimed by the lender. */ function claimableFunds() external view returns (uint256 claimableFunds_); /** * @dev The amount of collateral posted against outstanding (drawn down) principal. */ function collateral() external view returns (uint256 collateral_); /** * @dev The address of the asset deposited by the borrower as collateral, if needed. */ function collateralAsset() external view returns (address collateralAsset_); /** * @dev The amount of collateral required if all of the principal required is drawn down. */ function collateralRequired() external view returns (uint256 collateralRequired_); /** * @dev The delegate establishment fee. */ function delegateFee() external view returns (uint256 delegateFee_); /** * @dev The amount of funds that have yet to be drawn down by the borrower. */ function drawableFunds() external view returns (uint256 drawableFunds_); /** * @dev The rate charged at early payments. * This value should be configured so that it is less expensive to close a loan with more than one payment remaining, but * more expensive to close it if on the last payment. */ function earlyFeeRate() external view returns (uint256 earlyFeeRate_); /** * @dev The portion of principal to not be paid down as part of payment installments, which would need to be paid back upon final payment. * If endingPrincipal = principal, loan is interest-only. */ function endingPrincipal() external view returns (uint256 endingPrincipal_); /** * @dev The asset deposited by the lender to fund the loan. */ function fundsAsset() external view returns (address fundsAsset_); /** * @dev The amount of time the borrower has, after a payment is due, to make a payment before being in default. */ function gracePeriod() external view returns (uint256 gracePeriod_); /** * @dev The annualized interest rate (APR), in units of 1e18, (i.e. 1% is 0.01e18). */ function interestRate() external view returns (uint256 interestRate_); /** * @dev The rate charged at late payments. */ function lateFeeRate() external view returns (uint256 lateFeeRate_); /** * @dev The premium over the regular interest rate applied when paying late. */ function lateInterestPremium() external view returns (uint256 lateInterestPremium_); /** * @dev The lender of the Loan. */ function lender() external view returns (address lender_); /** * @dev The timestamp due date of the next payment. */ function nextPaymentDueDate() external view returns (uint256 nextPaymentDueDate_); /** * @dev The specified time between loan payments. */ function paymentInterval() external view returns (uint256 paymentInterval_); /** * @dev The number of payment installments remaining for the loan. */ function paymentsRemaining() external view returns (uint256 paymentsRemaining_); /** * @dev The address of the pending borrower. */ function pendingBorrower() external view returns (address pendingBorrower_); /** * @dev The address of the pending lender. */ function pendingLender() external view returns (address pendingLender_); /** * @dev The amount of principal owed (initially, the requested amount), which needs to be paid back. */ function principal() external view returns (uint256 principal_); /** * @dev The initial principal amount requested by the borrower. */ function principalRequested() external view returns (uint256 principalRequested_); /** * @dev The hash of the proposed refinance agreement. */ function refinanceCommitment() external view returns (bytes32 refinanceCommitment_); /** * @dev Amount of unpaid interest that has accrued before a refinance was accepted. */ function refinanceInterest() external view returns (uint256 refinanceInterest_); /** * @dev The factory address that deployed this contract (necessary for PoolV1 integration). */ function superFactory() external view returns (address superFactory_); /** * @dev The treasury establishment fee. */ function treasuryFee() external view returns (uint256 treasuryFee_); /********************************/ /*** State Changing Functions ***/ /********************************/ /** * @dev Accept the borrower role, must be called by pendingBorrower. */ function acceptBorrower() external; /** * @dev Accept the lender role, must be called by pendingLender. */ function acceptLender() external; /** * @dev Accept the proposed terms ans trigger refinance execution * @param refinancer_ The address of the refinancer contract. * @param deadline_ The deadline for accepting the new terms. * @param calls_ The encoded arguments to be passed to refinancer. * @param amount_ An amount to pull from the caller, if any. */ function acceptNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_, uint256 amount_) external; /** * @dev Claim funds that have been paid (principal, interest, and late fees). * @param amount_ The amount to be claimed. * @param destination_ The address to send the funds. */ function claimFunds(uint256 amount_, address destination_) external; /** * @dev Repay all principal and fees and close a loan. * @param amount_ An amount to pull from the caller, if any. * @return principal_ The portion of the amount paying back principal. * @return interest_ The portion of the amount paying interest. * @return delegateFee_ The portion of the amount paying establishment fees to the delegate. * @return treasuryFee_ The portion of the amount paying establishment fees to the treasury. */ function closeLoan(uint256 amount_) external returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_); /** * @dev Draw down funds from the loan. * @param amount_ The amount to draw down. * @param destination_ The address to send the funds. * @return collateralPosted_ The amount of additional collateral posted, if any. */ function drawdownFunds(uint256 amount_, address destination_) external returns (uint256 collateralPosted_); /** * @dev Lend funds to the loan/borrower. * @param lender_ The address to be registered as the lender. * @param amount_ An amount to pull from the caller, if any. * @return fundsLent_ The amount funded. */ function fundLoan(address lender_, uint256 amount_) external returns (uint256 fundsLent_); /** * @dev Make a payment to the loan. * @param amount_ An amount to pull from the caller, if any. * @return principal_ The portion of the amount paying back principal. * @return interest_ The portion of the amount paying interest fees. * @return delegateFee_ The portion of the amount paying establishment fees to the delegate. * @return treasuryFee_ The portion of the amount paying establishment fees to the treasury. */ function makePayment(uint256 amount_) external returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_); /** * @dev Post collateral to the loan. * @param amount_ An amount to pull from the caller, if any. * @return collateralPosted_ The amount posted. */ function postCollateral(uint256 amount_) external returns (uint256 collateralPosted_); /** * @dev Propose new terms for refinance * @param refinancer_ The address of the refinancer contract. * @param deadline_ The deadline for accepting the new terms. * @param calls_ The encoded arguments to be passed to refinancer. */ function proposeNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external; /** * @dev Nullify the current proposed terms. * @param refinancer_ The address of the refinancer contract. * @param deadline_ The deadline for accepting the new terms. * @param calls_ The encoded arguments to be passed to refinancer. */ function rejectNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external; /** * @dev Remove collateral from the loan (opposite of posting collateral). * @param amount_ The amount removed. * @param destination_ The destination to send the removed collateral. */ function removeCollateral(uint256 amount_, address destination_) external; /** * @dev Return funds to the loan (opposite of drawing down). * @param amount_ An amount to pull from the caller, if any. * @return fundsReturned_ The amount returned. */ function returnFunds(uint256 amount_) external returns (uint256 fundsReturned_); /** * @dev Repossess collateral, and any funds, for a loan in default. * @param destination_ The address where the collateral and funds asset is to be sent, if any. * @return collateralRepossessed_ The amount of collateral asset repossessed. * @return fundsRepossessed_ The amount of funds asset repossessed. */ function repossess(address destination_) external returns (uint256 collateralRepossessed_, uint256 fundsRepossessed_); /** * @dev Set the pendingBorrower to a new account. * @param pendingBorrower_ The address of the new pendingBorrower. */ function setPendingBorrower(address pendingBorrower_) external; /** * @dev Set the pendingLender to a new account. * @param pendingLender_ The address of the new pendingLender. */ function setPendingLender(address pendingLender_) external; /** * @dev Remove some token (neither fundsAsset nor collateralAsset) from the loan. * @param token_ The address of the token contract. * @param destination_ The recipient of the token. * @return skimmed_ The amount of token removed from the loan. */ function skim(address token_, address destination_) external returns (uint256 skimmed_); /**********************/ /*** View Functions ***/ /**********************/ /** * @dev Returns the excess collateral that can be removed. * @return excessCollateral_ The excess collateral that can be removed, if any. */ function excessCollateral() external view returns (uint256 excessCollateral_); /** * @dev Get the additional collateral to be posted to drawdown some amount. * @param drawdown_ The amount desired to be drawn down. * @return additionalCollateral_ The additional collateral that must be posted, if any. */ function getAdditionalCollateralRequiredFor(uint256 drawdown_) external view returns (uint256 additionalCollateral_); /** * @dev Get the breakdown of the total payment needed to satisfy an early repayment. * @return principal_ The portion of the total amount that will go towards principal. * @return interest_ The portion of the total amount that will go towards interest fees. * @return delegateFee_ The portion of the total amount that will go towards establishment fees to the delegate. * @return treasuryFee_ The portion of the total amount that will go towards establishment fees to the treasury. */ function getEarlyPaymentBreakdown() external view returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_); /** * @dev Get the breakdown of the total payment needed to satisfy the next payment installment. * @return principal_ The portion of the total amount that will go towards principal. * @return interest_ The portion of the total amount that will go towards interest fees. * @return delegateFee_ The portion of the total amount that will go towards establishment fees to the delegate. * @return treasuryFee_ The portion of the total amount that will go towards establishment fees to the treasury. */ function getNextPaymentBreakdown() external view returns (uint256 principal_, uint256 interest_, uint256 delegateFee_, uint256 treasuryFee_); /** * @dev Get the extra interest that will be charged according to loan terms before refinance, based on a given timestamp. * @param timestamp_ The timestamp when the new terms will be accepted. * @return proRataInterest_ The interest portion to be added in the next payment. */ function getRefinanceInterest(uint256 timestamp_) external view returns (uint256 proRataInterest_); /** * @dev Returns whether the protocol is paused. * @return paused_ A boolean indicating if protocol is paused. */ function isProtocolPaused() external view returns (bool paused_); }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; /// @title IMapleLoanEvents defines the events for a MapleLoan. interface IMapleLoanEvents { /** * @dev Borrower was accepted, and set to a new account. * @param borrower_ The address of the new borrower. */ event BorrowerAccepted(address indexed borrower_); /** * @dev Collateral was posted. * @param amount_ The amount of collateral posted. */ event CollateralPosted(uint256 amount_); /** * @dev Collateral was removed. * @param amount_ The amount of collateral removed. * @param destination_ The recipient of the collateral removed. */ event CollateralRemoved(uint256 amount_, address indexed destination_); /** * @dev Establishment fees were set. * @param delegateFee_ The amount that will be paid as an establishment fee to the delegate. * @param treasuryFee_ The amount that will be paid as an establishment fee to the treasury. */ event EstablishmentFeesSet(uint256 delegateFee_, uint256 treasuryFee_); /** * @dev The loan was funded. * @param lender_ The address of the lender. * @param amount_ The amount funded. * @param nextPaymentDueDate_ The due date of the next payment. */ event Funded(address indexed lender_, uint256 amount_, uint256 nextPaymentDueDate_); /** * @dev Funds were claimed. * @param amount_ The amount of funds claimed. * @param destination_ The recipient of the funds claimed. */ event FundsClaimed(uint256 amount_, address indexed destination_); /** * @dev Funds were drawn. * @param amount_ The amount of funds drawn. * @param destination_ The recipient of the funds drawn down. */ event FundsDrawnDown(uint256 amount_, address indexed destination_); /** * @dev Funds were redirected on an additional `fundLoan` call. * @param amount_ The amount of funds redirected. * @param destination_ The recipient of the redirected funds. */ event FundsRedirected(uint256 amount_, address indexed destination_); /** * @dev Funds were returned. * @param amount_ The amount of funds returned. */ event FundsReturned(uint256 amount_); /** * @dev The loan was initialized. * @param borrower_ The address of the borrower. * @param assets_ Array of asset addresses. * [0]: collateralAsset, * [1]: fundsAsset. * @param termDetails_ Array of loan parameters: * [0]: gracePeriod, * [1]: paymentInterval, * [2]: payments, * @param amounts_ Requested amounts: * [0]: collateralRequired, * [1]: principalRequested, * [2]: endingPrincipal. * @param rates_ Fee parameters: * [0]: interestRate, * [1]: earlyFeeRate, * [2]: lateFeeRate, * [3]: lateInterestPremium. */ event Initialized(address indexed borrower_, address[2] assets_, uint256[3] termDetails_, uint256[3] amounts_, uint256[4] rates_); /** * @dev Lender was accepted, and set to a new account. * @param lender_ The address of the new lender. */ event LenderAccepted(address indexed lender_); /** * @dev Loan was repaid early and closed. * @param principalPaid_ The portion of the total amount that went towards principal. * @param interestPaid_ The portion of the total amount that went towards interest. * @param delegateFeePaid_ The portion of the total amount that went towards the establishment fee for the delegate. * @param treasuryFeePaid_ The portion of the total amount that went towards the establishment fee for the treasury. */ event LoanClosed(uint256 principalPaid_, uint256 interestPaid_, uint256 delegateFeePaid_, uint256 treasuryFeePaid_); /** * @dev The terms of the refinance proposal were accepted. * @param refinanceCommitment_ The hash of the refinancer, deadline, and calls proposed. * @param refinancer_ The address that will execute the refinance. * @param deadline_ The deadline for accepting the new terms. * @param calls_ The individual calls for the refinancer contract. */ event NewTermsAccepted(bytes32 refinanceCommitment_, address refinancer_, uint256 deadline_, bytes[] calls_); /** * @dev A refinance was proposed. * @param refinanceCommitment_ The hash of the refinancer, deadline, and calls proposed. * @param refinancer_ The address that will execute the refinance. * @param deadline_ The deadline for accepting the new terms. * @param calls_ The individual calls for the refinancer contract. */ event NewTermsProposed(bytes32 refinanceCommitment_, address refinancer_, uint256 deadline_, bytes[] calls_); /** * @dev The terms of the refinance proposal were rejected. * @param refinanceCommitment_ The hash of the refinancer, deadline, and calls proposed. * @param refinancer_ The address that will execute the refinance. * @param deadline_ The deadline for accepting the new terms. * @param calls_ The individual calls for the refinancer contract. */ event NewTermsRejected(bytes32 refinanceCommitment_, address refinancer_, uint256 deadline_, bytes[] calls_); /** * @dev Payments were made. * @param principalPaid_ The portion of the total amount that went towards principal. * @param interestPaid_ The portion of the total amount that went towards interest. * @param delegateFeePaid_ The portion of the total amount that went towards the establishment fee for the delegate. * @param treasuryFeePaid_ The portion of the total amount that went towards the establishment fee for the treasury. */ event PaymentMade(uint256 principalPaid_, uint256 interestPaid_, uint256 delegateFeePaid_, uint256 treasuryFeePaid_); /** * @dev Pending borrower was set. * @param pendingBorrower_ Address that can accept the borrower role. */ event PendingBorrowerSet(address pendingBorrower_); /** * @dev Pending lender was set. * @param pendingLender_ Address that can accept the lender role. */ event PendingLenderSet(address pendingLender_); /** * @dev The loan was in default and funds and collateral was repossessed by the lender. * @param collateralRepossessed_ The amount of collateral asset repossessed. * @param fundsRepossessed_ The amount of funds asset repossessed. * @param destination_ The recipient of the collateral and funds, if any. */ event Repossessed(uint256 collateralRepossessed_, uint256 fundsRepossessed_, address indexed destination_); /** * @dev Some token (neither fundsAsset nor collateralAsset) was removed from the loan. * @param token_ The address of the token contract. * @param amount_ The amount of token remove from the loan. * @param destination_ The recipient of the token. */ event Skimmed(address indexed token_, uint256 amount_, address indexed destination_); }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; import { IMapleProxyFactory } from "../../modules/maple-proxy-factory/contracts/interfaces/IMapleProxyFactory.sol"; /// @title MapleLoanFactory deploys Loan instances. interface IMapleLoanFactory is IMapleProxyFactory { /** * @dev Whether the proxy is a MapleLoan deployed by this factory. * @param proxy_ The address of the proxy contract. * @return isLoan_ Whether the proxy is a MapleLoan deployed by this factory. */ function isLoan(address proxy_) external view returns (bool isLoan_); }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; interface ILenderLike { function poolDelegate() external view returns (address poolDelegate_); } interface IMapleGlobalsLike { /// @dev The address of the security admin function globalAdmin() external view returns (address globalAdmin_); /// @dev The address of the Governor responsible for management of global Maple variables. function governor() external view returns (address governor_); /// @dev The fee rate directed to Pool Delegates. function investorFee() external view returns (uint256 investorFee_); /// @dev The Treasury where all fees pass through for conversion, prior to distribution. function mapleTreasury() external view returns (address mapleTreasury_); /// @dev A boolean indicating whether the protocol is paused. function protocolPaused() external view returns (bool paused_); /// @dev The fee rate directed to the Maple Treasury. function treasuryFee() external view returns (uint256 treasuryFee_); }
// 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_); }
// 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))); } }
// 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_); }
// 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 {}
// 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; }
// 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_); }
// 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)))); } }
// 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_) } } }
// 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_); }
// 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; }
{ "remappings": [ "contract-test-utils/=modules/contract-test-utils/contracts/", "erc20-helper/=modules/erc20-helper/src/", "erc20/=modules/erc20/", "maple-proxy-factory/=modules/maple-proxy-factory/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower_","type":"address"}],"name":"BorrowerAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"CollateralPosted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"CollateralRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delegateFee_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"name":"EstablishmentFeesSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nextPaymentDueDate_","type":"uint256"}],"name":"Funded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"FundsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"FundsDrawnDown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"FundsRedirected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"FundsReturned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower_","type":"address"},{"indexed":false,"internalType":"address[2]","name":"assets_","type":"address[2]"},{"indexed":false,"internalType":"uint256[3]","name":"termDetails_","type":"uint256[3]"},{"indexed":false,"internalType":"uint256[3]","name":"amounts_","type":"uint256[3]"},{"indexed":false,"internalType":"uint256[4]","name":"rates_","type":"uint256[4]"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender_","type":"address"}],"name":"LenderAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"principalPaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestPaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegateFeePaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryFeePaid_","type":"uint256"}],"name":"LoanClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"},{"indexed":false,"internalType":"address","name":"refinancer_","type":"address"},{"indexed":false,"internalType":"uint256","name":"deadline_","type":"uint256"},{"indexed":false,"internalType":"bytes[]","name":"calls_","type":"bytes[]"}],"name":"NewTermsAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"},{"indexed":false,"internalType":"address","name":"refinancer_","type":"address"},{"indexed":false,"internalType":"uint256","name":"deadline_","type":"uint256"},{"indexed":false,"internalType":"bytes[]","name":"calls_","type":"bytes[]"}],"name":"NewTermsProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"},{"indexed":false,"internalType":"address","name":"refinancer_","type":"address"},{"indexed":false,"internalType":"uint256","name":"deadline_","type":"uint256"},{"indexed":false,"internalType":"bytes[]","name":"calls_","type":"bytes[]"}],"name":"NewTermsRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"principalPaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestPaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegateFeePaid_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryFeePaid_","type":"uint256"}],"name":"PaymentMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingBorrower_","type":"address"}],"name":"PendingBorrowerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingLender_","type":"address"}],"name":"PendingLenderSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"collateralRepossessed_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fundsRepossessed_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"Repossessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"}],"name":"Skimmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"toVersion_","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"arguments_","type":"bytes"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"acceptBorrower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptLender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"refinancer_","type":"address"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"bytes[]","name":"calls_","type":"bytes[]"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"acceptNewTerms","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrower","outputs":[{"internalType":"address","name":"borrower_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"destination_","type":"address"}],"name":"claimFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimableFunds","outputs":[{"internalType":"uint256","name":"claimableFunds_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"closeLoan","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"interest_","type":"uint256"},{"internalType":"uint256","name":"delegateFee_","type":"uint256"},{"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"uint256","name":"collateral_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralAsset","outputs":[{"internalType":"address","name":"collateralAsset_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralRequired","outputs":[{"internalType":"uint256","name":"collateralRequired_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delegateFee","outputs":[{"internalType":"uint256","name":"delegateFee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"drawableFunds","outputs":[{"internalType":"uint256","name":"drawableFunds_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"destination_","type":"address"}],"name":"drawdownFunds","outputs":[{"internalType":"uint256","name":"collateralPosted_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"earlyFeeRate","outputs":[{"internalType":"uint256","name":"earlyFeeRate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endingPrincipal","outputs":[{"internalType":"uint256","name":"endingPrincipal_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"excessCollateral","outputs":[{"internalType":"uint256","name":"excessCollateral_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"factory_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lender_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"fundLoan","outputs":[{"internalType":"uint256","name":"fundsLent_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fundsAsset","outputs":[{"internalType":"address","name":"fundsAsset_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"drawdown_","type":"uint256"}],"name":"getAdditionalCollateralRequiredFor","outputs":[{"internalType":"uint256","name":"collateral_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEarlyPaymentBreakdown","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"interest_","type":"uint256"},{"internalType":"uint256","name":"delegateFee_","type":"uint256"},{"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNextPaymentBreakdown","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"interest_","type":"uint256"},{"internalType":"uint256","name":"delegateFee_","type":"uint256"},{"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp_","type":"uint256"}],"name":"getRefinanceInterest","outputs":[{"internalType":"uint256","name":"proRataInterest_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gracePeriod","outputs":[{"internalType":"uint256","name":"gracePeriod_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"implementation_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestRate","outputs":[{"internalType":"uint256","name":"interestRate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isProtocolPaused","outputs":[{"internalType":"bool","name":"paused_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lateFeeRate","outputs":[{"internalType":"uint256","name":"lateFeeRate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lateInterestPremium","outputs":[{"internalType":"uint256","name":"lateInterestPremium_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lender","outputs":[{"internalType":"address","name":"lender_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"makePayment","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"},{"internalType":"uint256","name":"interest_","type":"uint256"},{"internalType":"uint256","name":"delegateFee_","type":"uint256"},{"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"migrator_","type":"address"},{"internalType":"bytes","name":"arguments_","type":"bytes"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextPaymentDueDate","outputs":[{"internalType":"uint256","name":"nextPaymentDueDate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentInterval","outputs":[{"internalType":"uint256","name":"paymentInterval_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentsRemaining","outputs":[{"internalType":"uint256","name":"paymentsRemaining_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingBorrower","outputs":[{"internalType":"address","name":"pendingBorrower_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingLender","outputs":[{"internalType":"address","name":"pendingLender_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"postCollateral","outputs":[{"internalType":"uint256","name":"collateralPosted_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"principal","outputs":[{"internalType":"uint256","name":"principal_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"principalRequested","outputs":[{"internalType":"uint256","name":"principalRequested_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"refinancer_","type":"address"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"bytes[]","name":"calls_","type":"bytes[]"}],"name":"proposeNewTerms","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refinanceCommitment","outputs":[{"internalType":"bytes32","name":"refinanceCommitment_","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"refinanceInterest","outputs":[{"internalType":"uint256","name":"refinanceInterest_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"refinancer_","type":"address"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"bytes[]","name":"calls_","type":"bytes[]"}],"name":"rejectNewTerms","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"destination_","type":"address"}],"name":"removeCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"destination_","type":"address"}],"name":"repossess","outputs":[{"internalType":"uint256","name":"collateralRepossessed_","type":"uint256"},{"internalType":"uint256","name":"fundsRepossessed_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"returnFunds","outputs":[{"internalType":"uint256","name":"fundsReturned_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation_","type":"address"}],"name":"setImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingBorrower_","type":"address"}],"name":"setPendingBorrower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingLender_","type":"address"}],"name":"setPendingLender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"destination_","type":"address"}],"name":"skim","outputs":[{"internalType":"uint256","name":"skimmed_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"superFactory","outputs":[{"internalType":"address","name":"superFactory_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasuryFee","outputs":[{"internalType":"uint256","name":"treasuryFee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"toVersion_","type":"uint256"},{"internalType":"bytes","name":"arguments_","type":"bytes"}],"name":"upgrade","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b5061379b806100206000396000f3fe608060405234801561001057600080fd5b50600436106103425760003560e01c80637c3a00fd116101b8578063ba5d307811610104578063d05951a0116100a2578063d8dfeb451161007c578063d8dfeb4514610634578063dac885611461063c578063e44b387514610654578063e920b1e11461065c57600080fd5b8063d05951a0146105fb578063d41ddc961461060e578063d784d4261461062157600080fd5b8063c3fbb6fd116100de578063c3fbb6fd146105cd578063c45a015514610368578063cc32d176146105e0578063ccc04484146105e857600080fd5b8063ba5d3078146105ac578063ba83276b146105b4578063bcead63e146105bc57600080fd5b8063a97d116111610171578063b69410de1161014b578063b69410de1461058c578063b86a513e14610594578063b96b5c991461059c578063b9b1f4e3146105a457600080fd5b8063a97d116114610560578063aabaecd614610568578063acb522b41461057957600080fd5b80637c3a00fd146105115780637df1f1b91461051957806387accaf11461052a5780638ffc92151461053d5780639e10320b14610545578063a06db7dc1461055857600080fd5b806345755dd6116102925780635eeb53b411610230578063712b772f1161020a578063712b772f146104e657806375a20676146104f957806377b3c55c146105015780637a0e6fa11461050957600080fd5b80635eeb53b4146104bc57806369458ba7146104cd578063700f5006146104d557600080fd5b806350acb4ee1161026c57806350acb4ee1461045b57806350f2012f1461046e5780635114cb52146104815780635c60da1b146104b457600080fd5b806345755dd61461040d57806347350e9f146104205780634eac42351461044857600080fd5b8063267f4ac3116102ff578063390d6855116102d9578063390d6855146103ce57806339ba9f86146103e15780633b99bcee146103f25780634003f34d1461040557600080fd5b8063267f4ac3146103ab5780632ead1098146103be57806330fea1ce146103c657600080fd5b806301daa38f146103475780630895326f146103515780630d49b38c146103685780630fe3d9b7146103885780631cc1cf46146103905780631f3f19ab14610398575b600080fd5b61034f61066f565b005b6013545b6040519081526020015b60405180910390f35b610370610715565b6040516001600160a01b03909116815260200161035f565b61034f610724565b600754610355565b61034f6103a636600461326c565b6107c7565b61034f6103b936600461326c565b61086c565b600b54610355565b600954610355565b61034f6103dc366004613474565b610908565b6005546001600160a01b0316610370565b61034f610400366004613499565b6109cb565b601254610355565b61035561041b366004613442565b610b3d565b61043361042e36600461326c565b610c12565b6040805192835260208301919091520161035f565b610355610456366004613442565b610cde565b61034f6104693660046133bc565b610d26565b61035561047c366004613442565b610f46565b61049461048f366004613442565b611002565b60408051948552602085019390935291830152606082015260800161035f565b610370611147565b6003546001600160a01b0316610370565b610494611151565b6002546001600160a01b0316610370565b6103556104f43660046132a6565b61116d565b600c54610355565b600a54610355565b610355611364565b600854610355565b6000546001600160a01b0316610370565b61034f610538366004613360565b6113a0565b600d54610355565b610355610553366004613442565b6114b5565b600654610355565b601654610355565b6004546001600160a01b0316610370565b61034f610587366004613360565b6114df565b601754610355565b600e54610355565b610494611590565b600f54610355565b601454610355565b601554610355565b6001546001600160a01b0316610370565b61034f6105db3660046132df565b61159e565b601854610355565b6103556105f6366004613474565b611643565b610494610609366004613442565b611752565b61034f61061c366004613474565b61188b565b61034f61062f36600461326c565b61194c565b601154610355565b6106446119d8565b604051901515815260200161035f565b601054610355565b61035561066a366004613334565b611ac2565b6002546001600160a01b031633146106ce5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a41423a4e4f545f50454e44494e475f424f52524f57455200000000000060448201526064015b60405180910390fd5b600280546001600160a01b031990811690915560008054339216821781556040517f29bac0ac2b15405bfcc160bb74b6ae7a559b7674ce33db80785ada73e38204d29190a2565b600061071f611c6a565b905090565b6003546001600160a01b0316331461077e5760405162461bcd60e51b815260206004820152601860248201527f4d4c3a414c3a4e4f545f50454e44494e475f4c454e444552000000000000000060448201526064016106c5565b600380546001600160a01b031990811690915560018054339216821790556040517fd6165838d2e3db87aa1002b548048673fc6427eefbd1b914e100f3a0deae23e390600090a2565b6000546001600160a01b031633146108175760405162461bcd60e51b815260206004820152601360248201527226a61d29a8211d2727aa2fa127a92927aba2a960691b60448201526064016106c5565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f10f06072822ef73860fedb88933f968d20bb4aadce8a8d360d1124cb6ce1e0b2906020015b60405180910390a150565b6001546001600160a01b031633146108ba5760405162461bcd60e51b815260206004820152601160248201527026a61d29a8261d2727aa2fa622a72222a960791b60448201526064016106c5565b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527fa3ab02442c80a4102475683f16513c9139a89142be9db9804edfcfbb379fc49290602001610861565b6109106119d8565b1561092d5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b0316331461097a5760405162461bcd60e51b815260206004820152601060248201526f26a61d21a31d2727aa2fa622a72222a960811b60448201526064016106c5565b806001600160a01b03167f6bd56533ce1c8ea03f7b858ac441b5a86d140a793a7c9e3faecbbe517c2c8791836040516109b591815260200190565b60405180910390a26109c78282611c99565b5050565b6109d3611d0c565b6001600160a01b031663ec9a93686040518163ffffffff1660e01b815260040160206040518083038186803b158015610a0b57600080fd5b505afa158015610a1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a439190613289565b6001600160a01b0316336001600160a01b031614610a945760405162461bcd60e51b815260206004820152600e60248201526d26a61d2a9d2727aa2fa0a226a4a760911b60448201526064016106c5565b7faaaa7ee6b0c2f4ee1fa7312c7d5b3623a434da5a1a9ce3cb6e629caa23454ab6838383604051610ac793929190613653565b60405180910390a1610ad7611c6a565b6001600160a01b031663fe69f7088484846040518463ffffffff1660e01b8152600401610b0693929190613653565b600060405180830381600087803b158015610b2057600080fd5b505af1158015610b34573d6000803e3d6000fd5b50505050505050565b6000610b476119d8565b15610b645760405162461bcd60e51b81526004016106c590613627565b811580610b845750600554610b84906001600160a01b0316333085611d86565b610bd05760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a52463a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b7f278e51d3323fbf18b9fb8df3f8b97e31b145bc1146c52e764cf4aa1bfc4ba17d610bf9611dfd565b60405181815290925060200160405180910390a1919050565b600080610c1d6119d8565b15610c3a5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b03163314610c865760405162461bcd60e51b815260206004820152600f60248201526e26a61d291d2727aa2fa622a72222a960891b60448201526064016106c5565b610c8f83611e33565b60408051838152602081018390529294509092506001600160a01b038516917f027e623aab0b174da270ff529cad1c54f09182651e07437d2ac557929b9e5b49910160405180910390a2915091565b600080610d0060145484600f54610cf5919061370d565b600d54600c54611fa8565b601154909150808211610d14576000610d1e565b610d1e818361370d565b949350505050565b610d2e6119d8565b15610d4b5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b0316338114610d9a5760405162461bcd60e51b815260206004820152601160248201527026a61d20a72a1d2727aa2fa622a72222a960791b60448201526064016106c5565b6005546001600160a01b0316821580610dba5750610dba81333086611d86565b610e065760405162461bcd60e51b815260206004820152601b60248201527f4d4c3a414e543a5452414e534645525f46524f4d5f4641494c4544000000000060448201526064016106c5565b7f7150c332bd889236b6ab42cc34f0853631ceb58827f58a8697b682f13e390a8c610e3388888888611fe1565b88888888604051610e489594939291906135f9565b60405180910390a17ffe9a32948c4b8ec5c8a8eddeacd3f3621458e8bde95b725b625e5c8f4f2cb54d601754601854604051610e8e929190918252602082015260400190565b60405180910390a16000610ea1826122e0565b90508015610f3c57826001600160a01b03167ff505854d1244de20a434e0eca67ec8de6d69504f7f85594c61102ab4d9a278f382604051610ee491815260200190565b60405180910390a2610ef78284836123ba565b610f3c5760405162461bcd60e51b815260206004820152601660248201527513530e9053950e9514905394d1915497d1905253115160521b60448201526064016106c5565b5050505050505050565b6000610f506119d8565b15610f6d5760405162461bcd60e51b81526004016106c590613627565b811580610f8d5750600454610f8d906001600160a01b0316333085611d86565b610fd95760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a50433a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b7f437d44b2c697fb69e2b2f25f57fd844e376c25ed28ed5a9c4be88aa1e5c87d12610bf96123f7565b6000806000806000600f54905060008614806110315750600554611031906001600160a01b0316333089611d86565b61107d5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a4d503a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b611085612423565b600054939850919650945092506001600160a01b03163314806110aa575080600f5410155b6110f65760405162461bcd60e51b815260206004820152601960248201527f4d4c3a4d503a43414e4e4f545f5553455f4452415741424c450000000000000060448201526064016106c5565b6040805186815260208101869052908101849052606081018390527f95c4acf903eb698cf367efaaf79a8a58fb4554fcd8503b62af9f9c1b68b59e1e906080015b60405180910390a1509193509193565b600061071f61251a565b60008060008061115f612544565b929791965094509092509050565b60006111776119d8565b156111945760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b03163314806111b757506001546001600160a01b031633145b6111f15760405162461bcd60e51b815260206004820152600b60248201526a09874a6749c9ebe82aaa8960ab1b60448201526064016106c5565b6005546001600160a01b0384811691161480159061121d57506004546001600160a01b03848116911614155b61125d5760405162461bcd60e51b8152602060048201526011602482015270261d299d24a72b20a624a22faa27a5a2a760791b60448201526064016106c5565b6040516370a0823160e01b81523060048201526001600160a01b0380841691908516907ff1f6a55e7ad487ac8dd8e1d4517348d3b410a7a0bc405ef87b09078dc51b23b69082906370a082319060240160206040518083038186803b1580156112c557600080fd5b505afa1580156112d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112fd919061345b565b60405181815290945060200160405180910390a361131c8383836123ba565b61135e5760405162461bcd60e51b8152602060048201526013602482015272130e94ce9514905394d1915497d19052531151606a1b60448201526064016106c5565b92915050565b60008061137b601454600f54600d54600c54611fa8565b60115490915081811161138f576000611399565b611399828261370d565b9250505090565b6113a86119d8565b156113c55760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146114155760405162461bcd60e51b815260206004820152601360248201527226a61d28272a1d2727aa2fa127a92927aba2a960691b60448201526064016106c5565b428310156114655760405162461bcd60e51b815260206004820152601760248201527f4d4c3a504e543a494e56414c49445f444541444c494e4500000000000000000060448201526064016106c5565b7ff94d2f0322894aaf1bce14561461a8b8b6c9b11a77bbe80f20b804da8a95e4b7611492858585856125ab565b858585856040516114a79594939291906135f9565b60405180910390a150505050565b60006114d882600754601454600e54600854601354601254600a54600b546125d3565b5092915050565b6114e76119d8565b156115045760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633148061152757506001546001600160a01b031633145b6115635760405162461bcd60e51b815260206004820152600d60248201526c09874a49ca8749c9ebe82aaa89609b1b60448201526064016106c5565b7f47244a449377da5fd10e98d86d118dee442e842fc34f05179c973cfcff6acba761149285858585612642565b60008060008061115f6126b0565b6115a6611c6a565b6001600160a01b0316336001600160a01b0316146115f95760405162461bcd60e51b815260206004820152601060248201526f4d4c3a4d3a4e4f545f464143544f525960801b60448201526064016106c5565b6116048383836126fb565b61163e5760405162461bcd60e51b815260206004820152600b60248201526a13530e934e91905253115160aa1b60448201526064016106c5565b505050565b600061164d6119d8565b1561166a5760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146116b95760405162461bcd60e51b815260206004820152601260248201527126a61d22231d2727aa2fa127a92927aba2a960711b60448201526064016106c5565b816001600160a01b03167f7578fe8c4d9f6fc38fdad20d219b0ce47d38bbf8a72bdb26867809f24119363d846040516116f491815260200190565b60405180910390a2600061170784610cde565b9050801561174857600454600090611727906001600160a01b03166122e0565b905061174481831161173a576000610f46565b61047c828461370d565b9250505b6114d88484612774565b6000806000806000600f54905060008614806117815750600554611781906001600160a01b0316333089611d86565b6117cd5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a434c3a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b6117d561283b565b600054939850919650945092506001600160a01b03163314806117fa575080600f5410155b6118465760405162461bcd60e51b815260206004820152601960248201527f4d4c3a434c3a43414e4e4f545f5553455f4452415741424c450000000000000060448201526064016106c5565b6040805186815260208101869052908101849052606081018390527f6d5b31efac20a15ed5b9e27e38cf9ebcc3ffb6d64feb827a35ef84a607e8dfaf90608001611137565b6118936119d8565b156118b05760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146118ff5760405162461bcd60e51b815260206004820152601260248201527126a61d29219d2727aa2fa127a92927aba2a960711b60448201526064016106c5565b806001600160a01b03167f97b446ee2df422b7273fe6d658674835f9de3319d131c229f9a2f8ed62a761938360405161193a91815260200190565b60405180910390a26109c78282612929565b611954611c6a565b6001600160a01b0316336001600160a01b0316146119a85760405162461bcd60e51b81526020600482015260116024820152704d4c3a53493a4e4f545f464143544f525960781b60448201526064016106c5565b6001600160a01b03167f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b50565b60006119e2611c6a565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a1a57600080fd5b505afa158015611a2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a529190613289565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8a57600080fd5b505afa158015611a9e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071f9190613420565b6000611acc6119d8565b15611ae95760405162461bcd60e51b81526004016106c590613627565b6005546001600160a01b0316821580611b095750611b0981333086611d86565b611b555760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a464c3a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b601254611baf57836001600160a01b03167fcd909ec339185c4598a4096e174308fbdf136d117f230960f873a2f2e81f63af611b90866129f0565b6012546040805183815260208101929092529195500160405180910390a25b6000611bba826122e0565b6001549091506001600160a01b03168115611c6157806001600160a01b03167ff505854d1244de20a434e0eca67ec8de6d69504f7f85594c61102ab4d9a278f383604051611c0a91815260200190565b60405180910390a2611c1d8382846123ba565b611c615760405162461bcd60e51b815260206004820152601560248201527413530e91930e9514905394d1915497d19052531151605a1b60448201526064016106c5565b50505092915050565b6000611c947f7a45a402e4cb6e08ebc196f20f66d5d30e67285a2a8aa80503fa409e727a4af15490565b919050565b8160106000828254611cab919061370d565b9091555050600554611cc7906001600160a01b031682846123ba565b6109c75760405162461bcd60e51b81526020600482015260166024820152751353124e90d18e9514905394d1915497d1905253115160521b60448201526064016106c5565b6000611d16611c6a565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d4e57600080fd5b505afa158015611d62573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071f9190613289565b6040516001600160a01b0380851660248301528316604482015260648101829052600090611df49086906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612b3a565b95945050505050565b600554600090611e15906001600160a01b03166122e0565b905080600f6000828254611e2991906136b4565b9250508190555090565b60125460009081908015801590611e555750600654611e5290826136b4565b42115b611e985760405162461bcd60e51b81526020600482015260146024820152731353124e948e9393d517d25397d111519055531560621b60448201526064016106c5565b611ea0612bda565b600060118190556010819055600f8190556004546001600160a01b031690611ec7826122e0565b9450841480611edc5750611edc8186866123ba565b611f285760405162461bcd60e51b815260206004820152601760248201527f4d4c493a523a435f5452414e534645525f4641494c454400000000000000000060448201526064016106c5565b6005546001600160a01b03166000611f3f826122e0565b9450841480611f545750611f548187866123ba565b611fa05760405162461bcd60e51b815260206004820152601760248201527f4d4c493a523a465f5452414e534645525f4641494c454400000000000000000060448201526064016106c5565b505050915091565b600083851115611fd65782611fbd858761370d565b611fc790846136ee565b611fd191906136cc565b611df4565b506000949350505050565b6000611fef85858585612c18565b905080601554146120425760405162461bcd60e51b815260206004820152601b60248201527f4d4c493a414e543a434f4d4d49544d454e545f4d49534d41544348000000000060448201526064016106c5565b6001600160a01b0385163b6120995760405162461bcd60e51b815260206004820152601a60248201527f4d4c493a414e543a494e56414c49445f524546494e414e43455200000000000060448201526064016106c5565b834211156120e95760405162461bcd60e51b815260206004820152601a60248201527f4d4c493a414e543a455850495245445f434f4d4d49544d454e5400000000000060448201526064016106c5565b600060075490506000806121124284601454600e54600854601354601254600a54600b546125d3565b91509150816016600082825461212891906136b4565b90915550506000601581905585905b818110156122085760008a6001600160a01b031689898481811061215d5761215d61373a565b905060200281019061216f919061366d565b60405161217d929190613586565b600060405180830381855af49150503d80600081146121b8576040519150601f19603f3d011682016040523d82523d6000602084013e6121bd565b606091505b50509050806121ff5760405162461bcd60e51b815260206004820152600e60248201526d1353124e9053950e91905253115160921b60448201526064016106c5565b50600101612137565b5060006013548561221991906136ee565b8360175461222791906136ee565b61223191906136cc565b905060006013548661224391906136ee565b8460185461225191906136ee565b61225b91906136cc565b60075490915061226b81426136b4565b60125560145461227d90828585612c51565b612285612daf565b6122d15760405162461bcd60e51b815260206004820152601f60248201527f4d4c493a414e543a494e53554646494349454e545f434f4c4c41544552414c0060448201526064016106c5565b50505050505050949350505050565b6005546000906001600160a01b038381169116146122ff57600061230f565b600f5460105461230f91906136b4565b6004546001600160a01b0384811691161461232b57600061232f565b6011545b6040516370a0823160e01b81523060048201526001600160a01b038516906370a082319060240160206040518083038186803b15801561236e57600080fd5b505afa158015612382573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a6919061345b565b6123b0919061370d565b61135e919061370d565b6040516001600160a01b0383166024820152604481018290526000906123ed90859063a9059cbb60e01b90606401611dbd565b90505b9392505050565b60045460009061240f906001600160a01b03166122e0565b90508060116000828254611e2991906136b4565b6000806000806124316126b0565b600060168190559397509195509350915061244c84866136b4565b90508161245984836136b4565b61246391906136b4565b600554612478906001600160a01b03166122e0565b600f5461248591906136b4565b61248f919061370d565b600f8190555080601060008282546124a791906136b4565b909155506124b790508383612dcf565b60135460018114156124d0576124cb612bda565b612512565b600754601260008282546124e491906136b4565b9250508190555085601460008282546124fd919061370d565b9091555061250e905060018261370d565b6013555b505090919293565b6000611c947f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b600080600080601654670de0b6b3a764000060095460145496508661256991906136ee565b61257391906136cc565b61257d91906136b4565b601354601754919450906125929082906136ee565b9250806018546125a291906136ee565b91505090919293565b6000816125b95760006125c5565b6125c585858585612c18565b601581905595945050505050565b600080846125e18b8d6136b4565b10156125f257506000905080612634565b6125fc8a8661370d565b612606908c61370d565b9050612615898989848a612e41565b925061262790508b8a89888888612f34565b61263190836136b4565b91505b995099975050505050505050565b600061265085858585612c18565b905080601554146126a35760405162461bcd60e51b815260206004820152601b60248201527f4d4c493a524e543a434f4d4d49544d454e545f4d49534d41544348000000000060448201526064016106c5565b6000601555949350505050565b6000806000806126d742601254600754601454600e54601354600854600a54600b54612fda565b60165491955093506126e990846136b4565b92506017549150601854905090919293565b6000833b8061270e5760009150506123f0565b846001600160a01b03168484604051612728929190613586565b600060405180830381855af49150503d8060008114612763576040519150601f19603f3d011682016040523d82523d6000602084013e612768565b606091505b50909695505050505050565b81600f6000828254612786919061370d565b90915550506005546127a2906001600160a01b031682846123ba565b6127e75760405162461bcd60e51b81526020600482015260166024820152751353124e91118e9514905394d1915497d1905253115160521b60448201526064016106c5565b6127ef612daf565b6109c75760405162461bcd60e51b815260206004820152601e60248201527f4d4c493a44463a494e53554646494349454e545f434f4c4c41544552414c000060448201526064016106c5565b60008060008060125442111561288c5760405162461bcd60e51b81526020600482015260166024820152754d4c493a434c3a5041594d454e545f49535f4c41544560501b60448201526064016106c5565b612894612544565b60006016819055939750919550935091506128af84866136b4565b9050816128bc84836136b4565b6128c691906136b4565b6005546128db906001600160a01b03166122e0565b600f546128e891906136b4565b6128f2919061370d565b600f81905550806010600082825461290a91906136b4565b9091555061291a90508383612dcf565b612922612bda565b5090919293565b816011600082825461293b919061370d565b9091555050600454612957906001600160a01b031682846123ba565b61299c5760405162461bcd60e51b81526020600482015260166024820152751353124e9490ce9514905394d1915497d1905253115160521b60448201526064016106c5565b6129a4612daf565b6109c75760405162461bcd60e51b815260206004820152601e60248201527f4d4c493a52433a494e53554646494349454e545f434f4c4c41544552414c000060448201526064016106c5565b60006001600160a01b038216612a405760405162461bcd60e51b815260206004820152601560248201527426a6249d23261d24a72b20a624a22fa622a72222a960591b60448201526064016106c5565b601354601254158015612a5257508015155b612a935760405162461bcd60e51b81526020600482015260126024820152714d4c493a464c3a4c4f414e5f41435449564560701b60448201526064016106c5565b600754600180546001600160a01b0319166001600160a01b038616179055612abb81426136b4565b601255600d5460148190556005549093506001600160a01b031683612adf826122e0565b1015612b2d5760405162461bcd60e51b815260206004820152601860248201527f4d4c493a464c3a57524f4e475f46554e445f414d4f554e54000000000000000060448201526064016106c5565b505050600f819055919050565b60006001600160a01b0383163b612b535750600061135e565b6060836001600160a01b031683604051612b6d9190613596565b6000604051808303816000865af19150503d8060008114612baa576040519150601f19603f3d011682016040523d82523d6000602084013e612baf565b606091505b509092509050818015610d1e575080511580610d1e575080806020019051810190610d1e9190613420565b60006006819055600781905560088190556009819055600a819055600b819055600e8190556012819055601381905560148190556017819055601855565b600084848484604051602001612c3194939291906135d1565b604051602081830303815290604052805190602001209050949350505050565b6000612c5b611d0c565b90508264496cebb80085836001600160a01b03166316a12d7a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612c9e57600080fd5b505afa158015612cb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cd6919061345b565b612ce090896136ee565b612cea91906136ee565b612cf491906136cc565b612cfe91906136b4565b6017819055508164496cebb80085836001600160a01b031663cc32d1766040518163ffffffff1660e01b815260040160206040518083038186803b158015612d4557600080fd5b505afa158015612d59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d7d919061345b565b612d8790896136ee565b612d9191906136ee565b612d9b91906136cc565b612da591906136b4565b6018555050505050565b6000612dc5601454600f54600d54600c54611fa8565b6011541015905090565b600154612ded906001600160a01b0316634046af2b60e01b84613029565b612e09578160106000828254612e0391906136b4565b90915550505b612e22612e14611d0c565b63a5a2760560e01b83613029565b6109c7578060106000828254612e3891906136b4565b90915550505050565b6000806000612e508686613138565b90506000612e78612e6983670de0b6b3a76400006136b4565b86670de0b6b3a7640000613153565b9050670de0b6b3a76400008111612eab5784612e94898b61370d565b612e9e91906136cc565b6000935093505050612f2a565b6000612ebf670de0b6b3a76400008361370d565b838a670de0b6b3a7640000612ed4868f6136ee565b612ede91906136cc565b612ee8919061370d565b612ef291906136ee565b612efc91906136cc565b9050612f098a89896131b5565b935083811015612f1a576000612f24565b612f24848261370d565b94505050505b9550959350505050565b6000838711612f4557506000612fd0565b6000620151806001612f57878b61370d565b612f61919061370d565b612f6b91906136cc565b612f769060016136b4565b612f8390620151806136ee565b9050612f9987612f9385896136b4565b836131b5565b612fa390836136b4565b9150670de0b6b3a7640000612fb888866136ee565b612fc291906136cc565b612fcc90836136b4565b9150505b9695505050505050565b600080612fea8888878c8a612e41565b909250905060018614612ffd5781612fff565b875b915061300f8b89878d8888612f34565b61301990826136b4565b9050995099975050505050505050565b600081613038575060016123f0565b60408051600481526024810182526020810180516001600160e01b03166001600160e01b03198716179052905160009182916001600160a01b0388169161307e91613596565b6000604051808303816000865af19150503d80600081146130bb576040519150601f19603f3d011682016040523d82523d6000602084013e6130c0565b606091505b50915091508115806130d457506020815114155b156130e4576000925050506123f0565b6000818060200190518101906130fa9190613289565b90506001600160a01b03811661311657600093505050506123f0565b60055461312d906001600160a01b031682876123ba565b979650505050505050565b60006301e1338061314983856136ee565b6123f091906136cc565b6000600183166131635781613165565b835b90505b60019290921c9182156123f0578161318085806136ee565b61318a91906136cc565b93506001831661319957613168565b816131a485836136ee565b6131ae91906136cc565b9050613168565b6000670de0b6b3a76400006131ca8484613138565b6131d490866136ee565b6123ed91906136cc565b60008083601f8401126131f057600080fd5b50813567ffffffffffffffff81111561320857600080fd5b6020830191508360208260051b850101111561322357600080fd5b9250929050565b60008083601f84011261323c57600080fd5b50813567ffffffffffffffff81111561325457600080fd5b60208301915083602082850101111561322357600080fd5b60006020828403121561327e57600080fd5b81356123f081613750565b60006020828403121561329b57600080fd5b81516123f081613750565b600080604083850312156132b957600080fd5b82356132c481613750565b915060208301356132d481613750565b809150509250929050565b6000806000604084860312156132f457600080fd5b83356132ff81613750565b9250602084013567ffffffffffffffff81111561331b57600080fd5b6133278682870161322a565b9497909650939450505050565b6000806040838503121561334757600080fd5b823561335281613750565b946020939093013593505050565b6000806000806060858703121561337657600080fd5b843561338181613750565b935060208501359250604085013567ffffffffffffffff8111156133a457600080fd5b6133b0878288016131de565b95989497509550505050565b6000806000806000608086880312156133d457600080fd5b85356133df81613750565b945060208601359350604086013567ffffffffffffffff81111561340257600080fd5b61340e888289016131de565b96999598509660600135949350505050565b60006020828403121561343257600080fd5b815180151581146123f057600080fd5b60006020828403121561345457600080fd5b5035919050565b60006020828403121561346d57600080fd5b5051919050565b6000806040838503121561348757600080fd5b8235915060208301356132d481613750565b6000806000604084860312156134ae57600080fd5b83359250602084013567ffffffffffffffff81111561331b57600080fd5b81835260006020808501808196508560051b810191508460005b878110156135505782840389528135601e1988360301811261350757600080fd5b8701803567ffffffffffffffff81111561352057600080fd5b80360389131561352f57600080fd5b61353c868289850161355d565b9a87019a95505050908401906001016134e6565b5091979650505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8183823760009101908152919050565b6000825160005b818110156135b7576020818601810151858301520161359d565b818111156135c6576000828501525b509190910192915050565b60018060a01b0385168152836020820152606060408201526000612fd06060830184866134cc565b85815260018060a01b038516602082015283604082015260806060820152600061312d6080830184866134cc565b60208082526012908201527113530e941493d513d0d3d317d4105554d15160721b604082015260600190565b838152604060208201526000611df460408301848661355d565b6000808335601e1984360301811261368457600080fd5b83018035915067ffffffffffffffff82111561369f57600080fd5b60200191503681900382131561322357600080fd5b600082198211156136c7576136c7613724565b500190565b6000826136e957634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561370857613708613724565b500290565b60008282101561371f5761371f613724565b500390565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6001600160a01b03811681146119d557600080fdfea264697066735822122024565e89cd77fea2f0418e44379d9ba6e10c7747e1f7e44e9e05d34842532dfe64736f6c63430008070033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103425760003560e01c80637c3a00fd116101b8578063ba5d307811610104578063d05951a0116100a2578063d8dfeb451161007c578063d8dfeb4514610634578063dac885611461063c578063e44b387514610654578063e920b1e11461065c57600080fd5b8063d05951a0146105fb578063d41ddc961461060e578063d784d4261461062157600080fd5b8063c3fbb6fd116100de578063c3fbb6fd146105cd578063c45a015514610368578063cc32d176146105e0578063ccc04484146105e857600080fd5b8063ba5d3078146105ac578063ba83276b146105b4578063bcead63e146105bc57600080fd5b8063a97d116111610171578063b69410de1161014b578063b69410de1461058c578063b86a513e14610594578063b96b5c991461059c578063b9b1f4e3146105a457600080fd5b8063a97d116114610560578063aabaecd614610568578063acb522b41461057957600080fd5b80637c3a00fd146105115780637df1f1b91461051957806387accaf11461052a5780638ffc92151461053d5780639e10320b14610545578063a06db7dc1461055857600080fd5b806345755dd6116102925780635eeb53b411610230578063712b772f1161020a578063712b772f146104e657806375a20676146104f957806377b3c55c146105015780637a0e6fa11461050957600080fd5b80635eeb53b4146104bc57806369458ba7146104cd578063700f5006146104d557600080fd5b806350acb4ee1161026c57806350acb4ee1461045b57806350f2012f1461046e5780635114cb52146104815780635c60da1b146104b457600080fd5b806345755dd61461040d57806347350e9f146104205780634eac42351461044857600080fd5b8063267f4ac3116102ff578063390d6855116102d9578063390d6855146103ce57806339ba9f86146103e15780633b99bcee146103f25780634003f34d1461040557600080fd5b8063267f4ac3146103ab5780632ead1098146103be57806330fea1ce146103c657600080fd5b806301daa38f146103475780630895326f146103515780630d49b38c146103685780630fe3d9b7146103885780631cc1cf46146103905780631f3f19ab14610398575b600080fd5b61034f61066f565b005b6013545b6040519081526020015b60405180910390f35b610370610715565b6040516001600160a01b03909116815260200161035f565b61034f610724565b600754610355565b61034f6103a636600461326c565b6107c7565b61034f6103b936600461326c565b61086c565b600b54610355565b600954610355565b61034f6103dc366004613474565b610908565b6005546001600160a01b0316610370565b61034f610400366004613499565b6109cb565b601254610355565b61035561041b366004613442565b610b3d565b61043361042e36600461326c565b610c12565b6040805192835260208301919091520161035f565b610355610456366004613442565b610cde565b61034f6104693660046133bc565b610d26565b61035561047c366004613442565b610f46565b61049461048f366004613442565b611002565b60408051948552602085019390935291830152606082015260800161035f565b610370611147565b6003546001600160a01b0316610370565b610494611151565b6002546001600160a01b0316610370565b6103556104f43660046132a6565b61116d565b600c54610355565b600a54610355565b610355611364565b600854610355565b6000546001600160a01b0316610370565b61034f610538366004613360565b6113a0565b600d54610355565b610355610553366004613442565b6114b5565b600654610355565b601654610355565b6004546001600160a01b0316610370565b61034f610587366004613360565b6114df565b601754610355565b600e54610355565b610494611590565b600f54610355565b601454610355565b601554610355565b6001546001600160a01b0316610370565b61034f6105db3660046132df565b61159e565b601854610355565b6103556105f6366004613474565b611643565b610494610609366004613442565b611752565b61034f61061c366004613474565b61188b565b61034f61062f36600461326c565b61194c565b601154610355565b6106446119d8565b604051901515815260200161035f565b601054610355565b61035561066a366004613334565b611ac2565b6002546001600160a01b031633146106ce5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a41423a4e4f545f50454e44494e475f424f52524f57455200000000000060448201526064015b60405180910390fd5b600280546001600160a01b031990811690915560008054339216821781556040517f29bac0ac2b15405bfcc160bb74b6ae7a559b7674ce33db80785ada73e38204d29190a2565b600061071f611c6a565b905090565b6003546001600160a01b0316331461077e5760405162461bcd60e51b815260206004820152601860248201527f4d4c3a414c3a4e4f545f50454e44494e475f4c454e444552000000000000000060448201526064016106c5565b600380546001600160a01b031990811690915560018054339216821790556040517fd6165838d2e3db87aa1002b548048673fc6427eefbd1b914e100f3a0deae23e390600090a2565b6000546001600160a01b031633146108175760405162461bcd60e51b815260206004820152601360248201527226a61d29a8211d2727aa2fa127a92927aba2a960691b60448201526064016106c5565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f10f06072822ef73860fedb88933f968d20bb4aadce8a8d360d1124cb6ce1e0b2906020015b60405180910390a150565b6001546001600160a01b031633146108ba5760405162461bcd60e51b815260206004820152601160248201527026a61d29a8261d2727aa2fa622a72222a960791b60448201526064016106c5565b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527fa3ab02442c80a4102475683f16513c9139a89142be9db9804edfcfbb379fc49290602001610861565b6109106119d8565b1561092d5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b0316331461097a5760405162461bcd60e51b815260206004820152601060248201526f26a61d21a31d2727aa2fa622a72222a960811b60448201526064016106c5565b806001600160a01b03167f6bd56533ce1c8ea03f7b858ac441b5a86d140a793a7c9e3faecbbe517c2c8791836040516109b591815260200190565b60405180910390a26109c78282611c99565b5050565b6109d3611d0c565b6001600160a01b031663ec9a93686040518163ffffffff1660e01b815260040160206040518083038186803b158015610a0b57600080fd5b505afa158015610a1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a439190613289565b6001600160a01b0316336001600160a01b031614610a945760405162461bcd60e51b815260206004820152600e60248201526d26a61d2a9d2727aa2fa0a226a4a760911b60448201526064016106c5565b7faaaa7ee6b0c2f4ee1fa7312c7d5b3623a434da5a1a9ce3cb6e629caa23454ab6838383604051610ac793929190613653565b60405180910390a1610ad7611c6a565b6001600160a01b031663fe69f7088484846040518463ffffffff1660e01b8152600401610b0693929190613653565b600060405180830381600087803b158015610b2057600080fd5b505af1158015610b34573d6000803e3d6000fd5b50505050505050565b6000610b476119d8565b15610b645760405162461bcd60e51b81526004016106c590613627565b811580610b845750600554610b84906001600160a01b0316333085611d86565b610bd05760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a52463a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b7f278e51d3323fbf18b9fb8df3f8b97e31b145bc1146c52e764cf4aa1bfc4ba17d610bf9611dfd565b60405181815290925060200160405180910390a1919050565b600080610c1d6119d8565b15610c3a5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b03163314610c865760405162461bcd60e51b815260206004820152600f60248201526e26a61d291d2727aa2fa622a72222a960891b60448201526064016106c5565b610c8f83611e33565b60408051838152602081018390529294509092506001600160a01b038516917f027e623aab0b174da270ff529cad1c54f09182651e07437d2ac557929b9e5b49910160405180910390a2915091565b600080610d0060145484600f54610cf5919061370d565b600d54600c54611fa8565b601154909150808211610d14576000610d1e565b610d1e818361370d565b949350505050565b610d2e6119d8565b15610d4b5760405162461bcd60e51b81526004016106c590613627565b6001546001600160a01b0316338114610d9a5760405162461bcd60e51b815260206004820152601160248201527026a61d20a72a1d2727aa2fa622a72222a960791b60448201526064016106c5565b6005546001600160a01b0316821580610dba5750610dba81333086611d86565b610e065760405162461bcd60e51b815260206004820152601b60248201527f4d4c3a414e543a5452414e534645525f46524f4d5f4641494c4544000000000060448201526064016106c5565b7f7150c332bd889236b6ab42cc34f0853631ceb58827f58a8697b682f13e390a8c610e3388888888611fe1565b88888888604051610e489594939291906135f9565b60405180910390a17ffe9a32948c4b8ec5c8a8eddeacd3f3621458e8bde95b725b625e5c8f4f2cb54d601754601854604051610e8e929190918252602082015260400190565b60405180910390a16000610ea1826122e0565b90508015610f3c57826001600160a01b03167ff505854d1244de20a434e0eca67ec8de6d69504f7f85594c61102ab4d9a278f382604051610ee491815260200190565b60405180910390a2610ef78284836123ba565b610f3c5760405162461bcd60e51b815260206004820152601660248201527513530e9053950e9514905394d1915497d1905253115160521b60448201526064016106c5565b5050505050505050565b6000610f506119d8565b15610f6d5760405162461bcd60e51b81526004016106c590613627565b811580610f8d5750600454610f8d906001600160a01b0316333085611d86565b610fd95760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a50433a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b7f437d44b2c697fb69e2b2f25f57fd844e376c25ed28ed5a9c4be88aa1e5c87d12610bf96123f7565b6000806000806000600f54905060008614806110315750600554611031906001600160a01b0316333089611d86565b61107d5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a4d503a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b611085612423565b600054939850919650945092506001600160a01b03163314806110aa575080600f5410155b6110f65760405162461bcd60e51b815260206004820152601960248201527f4d4c3a4d503a43414e4e4f545f5553455f4452415741424c450000000000000060448201526064016106c5565b6040805186815260208101869052908101849052606081018390527f95c4acf903eb698cf367efaaf79a8a58fb4554fcd8503b62af9f9c1b68b59e1e906080015b60405180910390a1509193509193565b600061071f61251a565b60008060008061115f612544565b929791965094509092509050565b60006111776119d8565b156111945760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b03163314806111b757506001546001600160a01b031633145b6111f15760405162461bcd60e51b815260206004820152600b60248201526a09874a6749c9ebe82aaa8960ab1b60448201526064016106c5565b6005546001600160a01b0384811691161480159061121d57506004546001600160a01b03848116911614155b61125d5760405162461bcd60e51b8152602060048201526011602482015270261d299d24a72b20a624a22faa27a5a2a760791b60448201526064016106c5565b6040516370a0823160e01b81523060048201526001600160a01b0380841691908516907ff1f6a55e7ad487ac8dd8e1d4517348d3b410a7a0bc405ef87b09078dc51b23b69082906370a082319060240160206040518083038186803b1580156112c557600080fd5b505afa1580156112d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112fd919061345b565b60405181815290945060200160405180910390a361131c8383836123ba565b61135e5760405162461bcd60e51b8152602060048201526013602482015272130e94ce9514905394d1915497d19052531151606a1b60448201526064016106c5565b92915050565b60008061137b601454600f54600d54600c54611fa8565b60115490915081811161138f576000611399565b611399828261370d565b9250505090565b6113a86119d8565b156113c55760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146114155760405162461bcd60e51b815260206004820152601360248201527226a61d28272a1d2727aa2fa127a92927aba2a960691b60448201526064016106c5565b428310156114655760405162461bcd60e51b815260206004820152601760248201527f4d4c3a504e543a494e56414c49445f444541444c494e4500000000000000000060448201526064016106c5565b7ff94d2f0322894aaf1bce14561461a8b8b6c9b11a77bbe80f20b804da8a95e4b7611492858585856125ab565b858585856040516114a79594939291906135f9565b60405180910390a150505050565b60006114d882600754601454600e54600854601354601254600a54600b546125d3565b5092915050565b6114e76119d8565b156115045760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633148061152757506001546001600160a01b031633145b6115635760405162461bcd60e51b815260206004820152600d60248201526c09874a49ca8749c9ebe82aaa89609b1b60448201526064016106c5565b7f47244a449377da5fd10e98d86d118dee442e842fc34f05179c973cfcff6acba761149285858585612642565b60008060008061115f6126b0565b6115a6611c6a565b6001600160a01b0316336001600160a01b0316146115f95760405162461bcd60e51b815260206004820152601060248201526f4d4c3a4d3a4e4f545f464143544f525960801b60448201526064016106c5565b6116048383836126fb565b61163e5760405162461bcd60e51b815260206004820152600b60248201526a13530e934e91905253115160aa1b60448201526064016106c5565b505050565b600061164d6119d8565b1561166a5760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146116b95760405162461bcd60e51b815260206004820152601260248201527126a61d22231d2727aa2fa127a92927aba2a960711b60448201526064016106c5565b816001600160a01b03167f7578fe8c4d9f6fc38fdad20d219b0ce47d38bbf8a72bdb26867809f24119363d846040516116f491815260200190565b60405180910390a2600061170784610cde565b9050801561174857600454600090611727906001600160a01b03166122e0565b905061174481831161173a576000610f46565b61047c828461370d565b9250505b6114d88484612774565b6000806000806000600f54905060008614806117815750600554611781906001600160a01b0316333089611d86565b6117cd5760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a434c3a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b6117d561283b565b600054939850919650945092506001600160a01b03163314806117fa575080600f5410155b6118465760405162461bcd60e51b815260206004820152601960248201527f4d4c3a434c3a43414e4e4f545f5553455f4452415741424c450000000000000060448201526064016106c5565b6040805186815260208101869052908101849052606081018390527f6d5b31efac20a15ed5b9e27e38cf9ebcc3ffb6d64feb827a35ef84a607e8dfaf90608001611137565b6118936119d8565b156118b05760405162461bcd60e51b81526004016106c590613627565b6000546001600160a01b031633146118ff5760405162461bcd60e51b815260206004820152601260248201527126a61d29219d2727aa2fa127a92927aba2a960711b60448201526064016106c5565b806001600160a01b03167f97b446ee2df422b7273fe6d658674835f9de3319d131c229f9a2f8ed62a761938360405161193a91815260200190565b60405180910390a26109c78282612929565b611954611c6a565b6001600160a01b0316336001600160a01b0316146119a85760405162461bcd60e51b81526020600482015260116024820152704d4c3a53493a4e4f545f464143544f525960781b60448201526064016106c5565b6001600160a01b03167f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b50565b60006119e2611c6a565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a1a57600080fd5b505afa158015611a2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a529190613289565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8a57600080fd5b505afa158015611a9e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071f9190613420565b6000611acc6119d8565b15611ae95760405162461bcd60e51b81526004016106c590613627565b6005546001600160a01b0316821580611b095750611b0981333086611d86565b611b555760405162461bcd60e51b815260206004820152601a60248201527f4d4c3a464c3a5452414e534645525f46524f4d5f4641494c454400000000000060448201526064016106c5565b601254611baf57836001600160a01b03167fcd909ec339185c4598a4096e174308fbdf136d117f230960f873a2f2e81f63af611b90866129f0565b6012546040805183815260208101929092529195500160405180910390a25b6000611bba826122e0565b6001549091506001600160a01b03168115611c6157806001600160a01b03167ff505854d1244de20a434e0eca67ec8de6d69504f7f85594c61102ab4d9a278f383604051611c0a91815260200190565b60405180910390a2611c1d8382846123ba565b611c615760405162461bcd60e51b815260206004820152601560248201527413530e91930e9514905394d1915497d19052531151605a1b60448201526064016106c5565b50505092915050565b6000611c947f7a45a402e4cb6e08ebc196f20f66d5d30e67285a2a8aa80503fa409e727a4af15490565b919050565b8160106000828254611cab919061370d565b9091555050600554611cc7906001600160a01b031682846123ba565b6109c75760405162461bcd60e51b81526020600482015260166024820152751353124e90d18e9514905394d1915497d1905253115160521b60448201526064016106c5565b6000611d16611c6a565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d4e57600080fd5b505afa158015611d62573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071f9190613289565b6040516001600160a01b0380851660248301528316604482015260648101829052600090611df49086906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612b3a565b95945050505050565b600554600090611e15906001600160a01b03166122e0565b905080600f6000828254611e2991906136b4565b9250508190555090565b60125460009081908015801590611e555750600654611e5290826136b4565b42115b611e985760405162461bcd60e51b81526020600482015260146024820152731353124e948e9393d517d25397d111519055531560621b60448201526064016106c5565b611ea0612bda565b600060118190556010819055600f8190556004546001600160a01b031690611ec7826122e0565b9450841480611edc5750611edc8186866123ba565b611f285760405162461bcd60e51b815260206004820152601760248201527f4d4c493a523a435f5452414e534645525f4641494c454400000000000000000060448201526064016106c5565b6005546001600160a01b03166000611f3f826122e0565b9450841480611f545750611f548187866123ba565b611fa05760405162461bcd60e51b815260206004820152601760248201527f4d4c493a523a465f5452414e534645525f4641494c454400000000000000000060448201526064016106c5565b505050915091565b600083851115611fd65782611fbd858761370d565b611fc790846136ee565b611fd191906136cc565b611df4565b506000949350505050565b6000611fef85858585612c18565b905080601554146120425760405162461bcd60e51b815260206004820152601b60248201527f4d4c493a414e543a434f4d4d49544d454e545f4d49534d41544348000000000060448201526064016106c5565b6001600160a01b0385163b6120995760405162461bcd60e51b815260206004820152601a60248201527f4d4c493a414e543a494e56414c49445f524546494e414e43455200000000000060448201526064016106c5565b834211156120e95760405162461bcd60e51b815260206004820152601a60248201527f4d4c493a414e543a455850495245445f434f4d4d49544d454e5400000000000060448201526064016106c5565b600060075490506000806121124284601454600e54600854601354601254600a54600b546125d3565b91509150816016600082825461212891906136b4565b90915550506000601581905585905b818110156122085760008a6001600160a01b031689898481811061215d5761215d61373a565b905060200281019061216f919061366d565b60405161217d929190613586565b600060405180830381855af49150503d80600081146121b8576040519150601f19603f3d011682016040523d82523d6000602084013e6121bd565b606091505b50509050806121ff5760405162461bcd60e51b815260206004820152600e60248201526d1353124e9053950e91905253115160921b60448201526064016106c5565b50600101612137565b5060006013548561221991906136ee565b8360175461222791906136ee565b61223191906136cc565b905060006013548661224391906136ee565b8460185461225191906136ee565b61225b91906136cc565b60075490915061226b81426136b4565b60125560145461227d90828585612c51565b612285612daf565b6122d15760405162461bcd60e51b815260206004820152601f60248201527f4d4c493a414e543a494e53554646494349454e545f434f4c4c41544552414c0060448201526064016106c5565b50505050505050949350505050565b6005546000906001600160a01b038381169116146122ff57600061230f565b600f5460105461230f91906136b4565b6004546001600160a01b0384811691161461232b57600061232f565b6011545b6040516370a0823160e01b81523060048201526001600160a01b038516906370a082319060240160206040518083038186803b15801561236e57600080fd5b505afa158015612382573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a6919061345b565b6123b0919061370d565b61135e919061370d565b6040516001600160a01b0383166024820152604481018290526000906123ed90859063a9059cbb60e01b90606401611dbd565b90505b9392505050565b60045460009061240f906001600160a01b03166122e0565b90508060116000828254611e2991906136b4565b6000806000806124316126b0565b600060168190559397509195509350915061244c84866136b4565b90508161245984836136b4565b61246391906136b4565b600554612478906001600160a01b03166122e0565b600f5461248591906136b4565b61248f919061370d565b600f8190555080601060008282546124a791906136b4565b909155506124b790508383612dcf565b60135460018114156124d0576124cb612bda565b612512565b600754601260008282546124e491906136b4565b9250508190555085601460008282546124fd919061370d565b9091555061250e905060018261370d565b6013555b505090919293565b6000611c947f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b600080600080601654670de0b6b3a764000060095460145496508661256991906136ee565b61257391906136cc565b61257d91906136b4565b601354601754919450906125929082906136ee565b9250806018546125a291906136ee565b91505090919293565b6000816125b95760006125c5565b6125c585858585612c18565b601581905595945050505050565b600080846125e18b8d6136b4565b10156125f257506000905080612634565b6125fc8a8661370d565b612606908c61370d565b9050612615898989848a612e41565b925061262790508b8a89888888612f34565b61263190836136b4565b91505b995099975050505050505050565b600061265085858585612c18565b905080601554146126a35760405162461bcd60e51b815260206004820152601b60248201527f4d4c493a524e543a434f4d4d49544d454e545f4d49534d41544348000000000060448201526064016106c5565b6000601555949350505050565b6000806000806126d742601254600754601454600e54601354600854600a54600b54612fda565b60165491955093506126e990846136b4565b92506017549150601854905090919293565b6000833b8061270e5760009150506123f0565b846001600160a01b03168484604051612728929190613586565b600060405180830381855af49150503d8060008114612763576040519150601f19603f3d011682016040523d82523d6000602084013e612768565b606091505b50909695505050505050565b81600f6000828254612786919061370d565b90915550506005546127a2906001600160a01b031682846123ba565b6127e75760405162461bcd60e51b81526020600482015260166024820152751353124e91118e9514905394d1915497d1905253115160521b60448201526064016106c5565b6127ef612daf565b6109c75760405162461bcd60e51b815260206004820152601e60248201527f4d4c493a44463a494e53554646494349454e545f434f4c4c41544552414c000060448201526064016106c5565b60008060008060125442111561288c5760405162461bcd60e51b81526020600482015260166024820152754d4c493a434c3a5041594d454e545f49535f4c41544560501b60448201526064016106c5565b612894612544565b60006016819055939750919550935091506128af84866136b4565b9050816128bc84836136b4565b6128c691906136b4565b6005546128db906001600160a01b03166122e0565b600f546128e891906136b4565b6128f2919061370d565b600f81905550806010600082825461290a91906136b4565b9091555061291a90508383612dcf565b612922612bda565b5090919293565b816011600082825461293b919061370d565b9091555050600454612957906001600160a01b031682846123ba565b61299c5760405162461bcd60e51b81526020600482015260166024820152751353124e9490ce9514905394d1915497d1905253115160521b60448201526064016106c5565b6129a4612daf565b6109c75760405162461bcd60e51b815260206004820152601e60248201527f4d4c493a52433a494e53554646494349454e545f434f4c4c41544552414c000060448201526064016106c5565b60006001600160a01b038216612a405760405162461bcd60e51b815260206004820152601560248201527426a6249d23261d24a72b20a624a22fa622a72222a960591b60448201526064016106c5565b601354601254158015612a5257508015155b612a935760405162461bcd60e51b81526020600482015260126024820152714d4c493a464c3a4c4f414e5f41435449564560701b60448201526064016106c5565b600754600180546001600160a01b0319166001600160a01b038616179055612abb81426136b4565b601255600d5460148190556005549093506001600160a01b031683612adf826122e0565b1015612b2d5760405162461bcd60e51b815260206004820152601860248201527f4d4c493a464c3a57524f4e475f46554e445f414d4f554e54000000000000000060448201526064016106c5565b505050600f819055919050565b60006001600160a01b0383163b612b535750600061135e565b6060836001600160a01b031683604051612b6d9190613596565b6000604051808303816000865af19150503d8060008114612baa576040519150601f19603f3d011682016040523d82523d6000602084013e612baf565b606091505b509092509050818015610d1e575080511580610d1e575080806020019051810190610d1e9190613420565b60006006819055600781905560088190556009819055600a819055600b819055600e8190556012819055601381905560148190556017819055601855565b600084848484604051602001612c3194939291906135d1565b604051602081830303815290604052805190602001209050949350505050565b6000612c5b611d0c565b90508264496cebb80085836001600160a01b03166316a12d7a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612c9e57600080fd5b505afa158015612cb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cd6919061345b565b612ce090896136ee565b612cea91906136ee565b612cf491906136cc565b612cfe91906136b4565b6017819055508164496cebb80085836001600160a01b031663cc32d1766040518163ffffffff1660e01b815260040160206040518083038186803b158015612d4557600080fd5b505afa158015612d59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d7d919061345b565b612d8790896136ee565b612d9191906136ee565b612d9b91906136cc565b612da591906136b4565b6018555050505050565b6000612dc5601454600f54600d54600c54611fa8565b6011541015905090565b600154612ded906001600160a01b0316634046af2b60e01b84613029565b612e09578160106000828254612e0391906136b4565b90915550505b612e22612e14611d0c565b63a5a2760560e01b83613029565b6109c7578060106000828254612e3891906136b4565b90915550505050565b6000806000612e508686613138565b90506000612e78612e6983670de0b6b3a76400006136b4565b86670de0b6b3a7640000613153565b9050670de0b6b3a76400008111612eab5784612e94898b61370d565b612e9e91906136cc565b6000935093505050612f2a565b6000612ebf670de0b6b3a76400008361370d565b838a670de0b6b3a7640000612ed4868f6136ee565b612ede91906136cc565b612ee8919061370d565b612ef291906136ee565b612efc91906136cc565b9050612f098a89896131b5565b935083811015612f1a576000612f24565b612f24848261370d565b94505050505b9550959350505050565b6000838711612f4557506000612fd0565b6000620151806001612f57878b61370d565b612f61919061370d565b612f6b91906136cc565b612f769060016136b4565b612f8390620151806136ee565b9050612f9987612f9385896136b4565b836131b5565b612fa390836136b4565b9150670de0b6b3a7640000612fb888866136ee565b612fc291906136cc565b612fcc90836136b4565b9150505b9695505050505050565b600080612fea8888878c8a612e41565b909250905060018614612ffd5781612fff565b875b915061300f8b89878d8888612f34565b61301990826136b4565b9050995099975050505050505050565b600081613038575060016123f0565b60408051600481526024810182526020810180516001600160e01b03166001600160e01b03198716179052905160009182916001600160a01b0388169161307e91613596565b6000604051808303816000865af19150503d80600081146130bb576040519150601f19603f3d011682016040523d82523d6000602084013e6130c0565b606091505b50915091508115806130d457506020815114155b156130e4576000925050506123f0565b6000818060200190518101906130fa9190613289565b90506001600160a01b03811661311657600093505050506123f0565b60055461312d906001600160a01b031682876123ba565b979650505050505050565b60006301e1338061314983856136ee565b6123f091906136cc565b6000600183166131635781613165565b835b90505b60019290921c9182156123f0578161318085806136ee565b61318a91906136cc565b93506001831661319957613168565b816131a485836136ee565b6131ae91906136cc565b9050613168565b6000670de0b6b3a76400006131ca8484613138565b6131d490866136ee565b6123ed91906136cc565b60008083601f8401126131f057600080fd5b50813567ffffffffffffffff81111561320857600080fd5b6020830191508360208260051b850101111561322357600080fd5b9250929050565b60008083601f84011261323c57600080fd5b50813567ffffffffffffffff81111561325457600080fd5b60208301915083602082850101111561322357600080fd5b60006020828403121561327e57600080fd5b81356123f081613750565b60006020828403121561329b57600080fd5b81516123f081613750565b600080604083850312156132b957600080fd5b82356132c481613750565b915060208301356132d481613750565b809150509250929050565b6000806000604084860312156132f457600080fd5b83356132ff81613750565b9250602084013567ffffffffffffffff81111561331b57600080fd5b6133278682870161322a565b9497909650939450505050565b6000806040838503121561334757600080fd5b823561335281613750565b946020939093013593505050565b6000806000806060858703121561337657600080fd5b843561338181613750565b935060208501359250604085013567ffffffffffffffff8111156133a457600080fd5b6133b0878288016131de565b95989497509550505050565b6000806000806000608086880312156133d457600080fd5b85356133df81613750565b945060208601359350604086013567ffffffffffffffff81111561340257600080fd5b61340e888289016131de565b96999598509660600135949350505050565b60006020828403121561343257600080fd5b815180151581146123f057600080fd5b60006020828403121561345457600080fd5b5035919050565b60006020828403121561346d57600080fd5b5051919050565b6000806040838503121561348757600080fd5b8235915060208301356132d481613750565b6000806000604084860312156134ae57600080fd5b83359250602084013567ffffffffffffffff81111561331b57600080fd5b81835260006020808501808196508560051b810191508460005b878110156135505782840389528135601e1988360301811261350757600080fd5b8701803567ffffffffffffffff81111561352057600080fd5b80360389131561352f57600080fd5b61353c868289850161355d565b9a87019a95505050908401906001016134e6565b5091979650505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8183823760009101908152919050565b6000825160005b818110156135b7576020818601810151858301520161359d565b818111156135c6576000828501525b509190910192915050565b60018060a01b0385168152836020820152606060408201526000612fd06060830184866134cc565b85815260018060a01b038516602082015283604082015260806060820152600061312d6080830184866134cc565b60208082526012908201527113530e941493d513d0d3d317d4105554d15160721b604082015260600190565b838152604060208201526000611df460408301848661355d565b6000808335601e1984360301811261368457600080fd5b83018035915067ffffffffffffffff82111561369f57600080fd5b60200191503681900382131561322357600080fd5b600082198211156136c7576136c7613724565b500190565b6000826136e957634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561370857613708613724565b500290565b60008282101561371f5761371f613724565b500390565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6001600160a01b03811681146119d557600080fdfea264697066735822122024565e89cd77fea2f0418e44379d9ba6e10c7747e1f7e44e9e05d34842532dfe64736f6c63430008070033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.