Feature Tip: Add private address tag to any address under My Name Tag !
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
12816672 | 1306 days ago | Contract Creation | 0 ETH |
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:
Loan
Compiler Version
v0.6.11+commit.5ef660b1
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2021-07-13 */ // SPDX-License-Identifier: AGPL-3.0-or-later // hevm: flattened sources of contracts/Loan.sol pragma solidity =0.6.11 >=0.6.0 <0.8.0 >=0.6.2 <0.8.0; ////// contracts/interfaces/ICollateralLocker.sol /* pragma solidity 0.6.11; */ interface ICollateralLocker { function collateralAsset() external view returns (address); function loan() external view returns (address); function pull(address, uint256) external; } ////// contracts/interfaces/ICollateralLockerFactory.sol /* pragma solidity 0.6.11; */ interface ICollateralLockerFactory { function owner(address) external view returns (address); function isLocker(address) external view returns (bool); function factoryType() external view returns (uint8); function newLocker(address) external returns (address); } ////// lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol /* pragma solidity >=0.6.0 <0.8.0; */ /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } ////// contracts/interfaces/IERC20Details.sol /* pragma solidity 0.6.11; */ /* import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; */ interface IERC20Details is IERC20 { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint256); } ////// contracts/interfaces/IFundingLocker.sol /* pragma solidity 0.6.11; */ interface IFundingLocker { function liquidityAsset() external view returns (address); function loan() external view returns (address); function pull(address, uint256) external; function drain() external; } ////// contracts/interfaces/IFundingLockerFactory.sol /* pragma solidity 0.6.11; */ interface IFundingLockerFactory { function owner(address) external view returns (address); function isLocker(address) external view returns (bool); function factoryType() external view returns (uint8); function newLocker(address) external returns (address); } ////// contracts/interfaces/ILateFeeCalc.sol /* pragma solidity 0.6.11; */ interface ILateFeeCalc { function calcType() external view returns (uint8); function name() external view returns (bytes32); function lateFee() external view returns (uint256); function getLateFee(uint256) external view returns (uint256); } ////// contracts/interfaces/ILiquidityLocker.sol /* pragma solidity 0.6.11; */ interface ILiquidityLocker { function pool() external view returns (address); function liquidityAsset() external view returns (address); function transfer(address, uint256) external; function fundLoan(address, address, uint256) external; } ////// contracts/interfaces/ILoanFactory.sol /* pragma solidity 0.6.11; */ interface ILoanFactory { function CL_FACTORY() external view returns (uint8); function FL_FACTORY() external view returns (uint8); function INTEREST_CALC_TYPE() external view returns (uint8); function LATEFEE_CALC_TYPE() external view returns (uint8); function PREMIUM_CALC_TYPE() external view returns (uint8); function globals() external view returns (address); function loansCreated() external view returns (uint256); function loans(uint256) external view returns (address); function isLoan(address) external view returns (bool); function loanFactoryAdmins(address) external view returns (bool); function setGlobals(address) external; function createLoan(address, address, address, address, uint256[5] memory, address[3] memory) external returns (address); function setLoanFactoryAdmin(address, bool) external; function pause() external; function unpause() external; } ////// contracts/interfaces/IMapleGlobals.sol /* pragma solidity 0.6.11; */ interface IMapleGlobals { function pendingGovernor() external view returns (address); function governor() external view returns (address); function globalAdmin() external view returns (address); function mpl() external view returns (address); function mapleTreasury() external view returns (address); function isValidBalancerPool(address) external view returns (bool); function treasuryFee() external view returns (uint256); function investorFee() external view returns (uint256); function defaultGracePeriod() external view returns (uint256); function fundingPeriod() external view returns (uint256); function swapOutRequired() external view returns (uint256); function isValidLiquidityAsset(address) external view returns (bool); function isValidCollateralAsset(address) external view returns (bool); function isValidPoolDelegate(address) external view returns (bool); function validCalcs(address) external view returns (bool); function isValidCalc(address, uint8) external view returns (bool); function getLpCooldownParams() external view returns (uint256, uint256); function isValidLoanFactory(address) external view returns (bool); function isValidSubFactory(address, address, uint8) external view returns (bool); function isValidPoolFactory(address) external view returns (bool); function getLatestPrice(address) external view returns (uint256); function defaultUniswapPath(address, address) external view returns (address); function minLoanEquity() external view returns (uint256); function maxSwapSlippage() external view returns (uint256); function protocolPaused() external view returns (bool); function stakerCooldownPeriod() external view returns (uint256); function lpCooldownPeriod() external view returns (uint256); function stakerUnstakeWindow() external view returns (uint256); function lpWithdrawWindow() external view returns (uint256); function oracleFor(address) external view returns (address); function validSubFactories(address, address) external view returns (bool); function setStakerCooldownPeriod(uint256) external; function setLpCooldownPeriod(uint256) external; function setStakerUnstakeWindow(uint256) external; function setLpWithdrawWindow(uint256) external; function setMaxSwapSlippage(uint256) external; function setGlobalAdmin(address) external; function setValidBalancerPool(address, bool) external; function setProtocolPause(bool) external; function setValidPoolFactory(address, bool) external; function setValidLoanFactory(address, bool) external; function setValidSubFactory(address, address, bool) external; function setDefaultUniswapPath(address, address, address) external; function setPoolDelegateAllowlist(address, bool) external; function setCollateralAsset(address, bool) external; function setLiquidityAsset(address, bool) external; function setCalc(address, bool) external; function setInvestorFee(uint256) external; function setTreasuryFee(uint256) external; function setMapleTreasury(address) external; function setDefaultGracePeriod(uint256) external; function setMinLoanEquity(uint256) external; function setFundingPeriod(uint256) external; function setSwapOutRequired(uint256) external; function setPriceOracle(address, address) external; function setPendingGovernor(address) external; function acceptGovernor() external; } ////// contracts/token/interfaces/IBaseFDT.sol /* pragma solidity 0.6.11; */ interface IBaseFDT { /** @dev Returns the total amount of funds a given address is able to withdraw currently. @param owner Address of FDT holder. @return A uint256 representing the available funds for a given account. */ function withdrawableFundsOf(address owner) external view returns (uint256); /** @dev Withdraws all available funds for a FDT holder. */ function withdrawFunds() external; /** @dev This event emits when new funds are distributed. @param by The address of the sender that distributed funds. @param fundsDistributed The amount of funds received for distribution. */ event FundsDistributed(address indexed by, uint256 fundsDistributed); /** @dev This event emits when distributed funds are withdrawn by a token holder. @param by The address of the receiver of funds. @param fundsWithdrawn The amount of funds that were withdrawn. @param totalWithdrawn The total amount of funds that were withdrawn. */ event FundsWithdrawn(address indexed by, uint256 fundsWithdrawn, uint256 totalWithdrawn); } ////// contracts/token/interfaces/IBasicFDT.sol /* pragma solidity 0.6.11; */ /* import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; */ /* import "./IBaseFDT.sol"; */ interface IBasicFDT is IBaseFDT, IERC20 { event PointsPerShareUpdated(uint256); event PointsCorrectionUpdated(address indexed, int256); function withdrawnFundsOf(address) external view returns (uint256); function accumulativeFundsOf(address) external view returns (uint256); function updateFundsReceived() external; } ////// contracts/token/interfaces/IExtendedFDT.sol /* pragma solidity 0.6.11; */ /* import "./IBasicFDT.sol"; */ interface IExtendedFDT is IBasicFDT { event LossesPerShareUpdated(uint256); event LossesCorrectionUpdated(address indexed, int256); event LossesDistributed(address indexed, uint256); event LossesRecognized(address indexed, uint256, uint256); function lossesPerShare() external view returns (uint256); function recognizableLossesOf(address) external view returns (uint256); function recognizedLossesOf(address) external view returns (uint256); function accumulativeLossesOf(address) external view returns (uint256); function updateLossesReceived() external; } ////// contracts/token/interfaces/IPoolFDT.sol /* pragma solidity 0.6.11; */ /* import "./IExtendedFDT.sol"; */ interface IPoolFDT is IExtendedFDT { function interestSum() external view returns (uint256); function poolLosses() external view returns (uint256); function interestBalance() external view returns (uint256); function lossesBalance() external view returns (uint256); } ////// contracts/interfaces/IPool.sol /* pragma solidity 0.6.11; */ /* import "../token/interfaces/IPoolFDT.sol"; */ interface IPool is IPoolFDT { function poolDelegate() external view returns (address); function poolAdmins(address) external view returns (bool); function deposit(uint256) external; function increaseCustodyAllowance(address, uint256) external; function transferByCustodian(address, address, uint256) external; function poolState() external view returns (uint256); function deactivate() external; function finalize() external; function claim(address, address) external returns (uint256[7] memory); function setLockupPeriod(uint256) external; function setStakingFee(uint256) external; function setPoolAdmin(address, bool) external; function fundLoan(address, address, uint256) external; function withdraw(uint256) external; function superFactory() external view returns (address); function triggerDefault(address, address) external; function isPoolFinalized() external view returns (bool); function setOpenToPublic(bool) external; function setAllowList(address, bool) external; function allowedLiquidityProviders(address) external view returns (bool); function openToPublic() external view returns (bool); function intendToWithdraw() external; function DL_FACTORY() external view returns (uint8); function liquidityAsset() external view returns (address); function liquidityLocker() external view returns (address); function stakeAsset() external view returns (address); function stakeLocker() external view returns (address); function stakingFee() external view returns (uint256); function delegateFee() external view returns (uint256); function principalOut() external view returns (uint256); function liquidityCap() external view returns (uint256); function lockupPeriod() external view returns (uint256); function depositDate(address) external view returns (uint256); function debtLockers(address, address) external view returns (address); function withdrawCooldown(address) external view returns (uint256); function setLiquidityCap(uint256) external; function cancelWithdraw() external; function reclaimERC20(address) external; function BPTVal(address, address, address, address) external view returns (uint256); function isDepositAllowed(uint256) external view returns (bool); function getInitialStakeRequirements() external view returns (uint256, uint256, bool, uint256, uint256); } ////// contracts/interfaces/IPoolFactory.sol /* pragma solidity 0.6.11; */ interface IPoolFactory { function LL_FACTORY() external view returns (uint8); function SL_FACTORY() external view returns (uint8); function poolsCreated() external view returns (uint256); function globals() external view returns (address); function pools(uint256) external view returns (address); function isPool(address) external view returns (bool); function poolFactoryAdmins(address) external view returns (bool); function setGlobals(address) external; function createPool(address, address, address, address, uint256, uint256, uint256) external returns (address); function setPoolFactoryAdmin(address, bool) external; function pause() external; function unpause() external; } ////// contracts/interfaces/IPremiumCalc.sol /* pragma solidity 0.6.11; */ interface IPremiumCalc { function calcType() external view returns (uint8); function name() external view returns (bytes32); function premiumFee() external view returns (uint256); function getPremiumPayment(address) external view returns (uint256, uint256, uint256); } ////// contracts/interfaces/IRepaymentCalc.sol /* pragma solidity 0.6.11; */ interface IRepaymentCalc { function calcType() external view returns (uint8); function name() external view returns (bytes32); function getNextPayment(address) external view returns (uint256, uint256, uint256); } ////// contracts/interfaces/IUniswapRouter.sol /* pragma solidity 0.6.11; */ interface IUniswapRouter { function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapETHForExactTokens( uint256 amountOut, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) external pure returns (uint256 amountB); function WETH() external pure returns (address); } ////// lib/openzeppelin-contracts/contracts/math/SafeMath.sol /* pragma solidity >=0.6.0 <0.8.0; */ /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } ////// contracts/library/Util.sol /* pragma solidity 0.6.11; */ /* import "../interfaces/IERC20Details.sol"; */ /* import "../interfaces/IMapleGlobals.sol"; */ /* import "lib/openzeppelin-contracts/contracts/math/SafeMath.sol"; */ /// @title Util is a library that contains utility functions. library Util { using SafeMath for uint256; /** @dev Calculates the minimum amount from a swap (adjustable for price slippage). @param globals Instance of a MapleGlobals. @param fromAsset Address of ERC-20 that will be swapped. @param toAsset Address of ERC-20 that will returned from swap. @param swapAmt Amount of `fromAsset` to be swapped. @return Expected amount of `toAsset` to receive from swap based on current oracle prices. */ function calcMinAmount(IMapleGlobals globals, address fromAsset, address toAsset, uint256 swapAmt) external view returns (uint256) { return swapAmt .mul(globals.getLatestPrice(fromAsset)) // Convert from `fromAsset` value. .mul(10 ** IERC20Details(toAsset).decimals()) // Convert to `toAsset` decimal precision. .div(globals.getLatestPrice(toAsset)) // Convert to `toAsset` value. .div(10 ** IERC20Details(fromAsset).decimals()); // Convert from `fromAsset` decimal precision. } } ////// lib/openzeppelin-contracts/contracts/utils/Address.sol /* pragma solidity >=0.6.2 <0.8.0; */ /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } ////// lib/openzeppelin-contracts/contracts/token/ERC20/SafeERC20.sol /* pragma solidity >=0.6.0 <0.8.0; */ /* import "./IERC20.sol"; */ /* import "../../math/SafeMath.sol"; */ /* import "../../utils/Address.sol"; */ /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } ////// contracts/library/LoanLib.sol /* pragma solidity 0.6.11; */ /* import "../interfaces/ICollateralLocker.sol"; */ /* import "../interfaces/ICollateralLockerFactory.sol"; */ /* import "../interfaces/IERC20Details.sol"; */ /* import "../interfaces/IFundingLocker.sol"; */ /* import "../interfaces/IFundingLockerFactory.sol"; */ /* import "../interfaces/IMapleGlobals.sol"; */ /* import "../interfaces/ILateFeeCalc.sol"; */ /* import "../interfaces/ILoanFactory.sol"; */ /* import "../interfaces/IPremiumCalc.sol"; */ /* import "../interfaces/IRepaymentCalc.sol"; */ /* import "../interfaces/IUniswapRouter.sol"; */ /* import "../library/Util.sol"; */ /* import "lib/openzeppelin-contracts/contracts/token/ERC20/SafeERC20.sol"; */ /* import "lib/openzeppelin-contracts/contracts/math/SafeMath.sol"; */ /// @title LoanLib is a library of utility functions used by Loan. library LoanLib { using SafeMath for uint256; using SafeERC20 for IERC20; address public constant UNISWAP_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; /********************************/ /*** Lender Utility Functions ***/ /********************************/ /** @dev Performs sanity checks on the data passed in Loan constructor. @param globals Instance of a MapleGlobals. @param liquidityAsset Contract address of the Liquidity Asset. @param collateralAsset Contract address of the Collateral Asset. @param specs Contains specifications for this Loan. */ function loanSanityChecks(IMapleGlobals globals, address liquidityAsset, address collateralAsset, uint256[5] calldata specs) external view { require(globals.isValidLiquidityAsset(liquidityAsset), "L:INVALID_LIQ_ASSET"); require(globals.isValidCollateralAsset(collateralAsset), "L:INVALID_COL_ASSET"); require(specs[2] != uint256(0), "L:ZERO_PID"); require(specs[1].mod(specs[2]) == uint256(0), "L:INVALID_TERM_DAYS"); require(specs[3] > uint256(0), "L:ZERO_REQUEST_AMT"); } /** @dev Returns capital to Lenders, if the Borrower has not drawn down the Loan past the grace period. @param liquidityAsset IERC20 of the Liquidity Asset. @param fundingLocker Address of FundingLocker. @param createdAt Timestamp of Loan instantiation. @param fundingPeriod Duration of the funding period, after which funds can be reclaimed. @return excessReturned Amount of Liquidity Asset that was returned to the Loan from the FundingLocker. */ function unwind(IERC20 liquidityAsset, address fundingLocker, uint256 createdAt, uint256 fundingPeriod) external returns (uint256 excessReturned) { // Only callable if Loan funding period has elapsed. require(block.timestamp > createdAt.add(fundingPeriod), "L:STILL_FUNDING_PERIOD"); // Account for existing balance in Loan. uint256 preBal = liquidityAsset.balanceOf(address(this)); // Drain funding from FundingLocker, transfers all the Liquidity Asset to this Loan. IFundingLocker(fundingLocker).drain(); return liquidityAsset.balanceOf(address(this)).sub(preBal); } /** @dev Liquidates a Borrower's collateral, via Uniswap, when a default is triggered. Only the Loan can call this function. @param collateralAsset IERC20 of the Collateral Asset. @param liquidityAsset Address of Liquidity Asset. @param superFactory Factory that instantiated Loan. @param collateralLocker Address of CollateralLocker. @return amountLiquidated Amount of Collateral Asset that was liquidated. @return amountRecovered Amount of Liquidity Asset that was returned to the Loan from the liquidation. */ function liquidateCollateral( IERC20 collateralAsset, address liquidityAsset, address superFactory, address collateralLocker ) external returns ( uint256 amountLiquidated, uint256 amountRecovered ) { // Get the liquidation amount from CollateralLocker. uint256 liquidationAmt = collateralAsset.balanceOf(address(collateralLocker)); // Pull the Collateral Asset from CollateralLocker. ICollateralLocker(collateralLocker).pull(address(this), liquidationAmt); if (address(collateralAsset) == liquidityAsset || liquidationAmt == uint256(0)) return (liquidationAmt, liquidationAmt); collateralAsset.safeApprove(UNISWAP_ROUTER, uint256(0)); collateralAsset.safeApprove(UNISWAP_ROUTER, liquidationAmt); IMapleGlobals globals = _globals(superFactory); // Get minimum amount of loan asset get after swapping collateral asset. uint256 minAmount = Util.calcMinAmount(globals, address(collateralAsset), liquidityAsset, liquidationAmt); // Generate Uniswap path. address uniswapAssetForPath = globals.defaultUniswapPath(address(collateralAsset), liquidityAsset); bool middleAsset = uniswapAssetForPath != liquidityAsset && uniswapAssetForPath != address(0); address[] memory path = new address[](middleAsset ? 3 : 2); path[0] = address(collateralAsset); path[1] = middleAsset ? uniswapAssetForPath : liquidityAsset; if (middleAsset) path[2] = liquidityAsset; // Swap collateralAsset for Liquidity Asset. uint256[] memory returnAmounts = IUniswapRouter(UNISWAP_ROUTER).swapExactTokensForTokens( liquidationAmt, minAmount.sub(minAmount.mul(globals.maxSwapSlippage()).div(10_000)), path, address(this), block.timestamp ); return(returnAmounts[0], returnAmounts[path.length - 1]); } /**********************************/ /*** Governor Utility Functions ***/ /**********************************/ /** @dev Transfers any locked funds to the Governor. Only the Governor can call this function. @param token Address of the token to be reclaimed. @param liquidityAsset Address of token that is used by the loan for drawdown and payments. @param globals Instance of a MapleGlobals. */ function reclaimERC20(address token, address liquidityAsset, IMapleGlobals globals) external { require(msg.sender == globals.governor(), "L:NOT_GOV"); require(token != liquidityAsset && token != address(0), "L:INVALID_TOKEN"); IERC20(token).safeTransfer(msg.sender, IERC20(token).balanceOf(address(this))); } /************************/ /*** Getter Functions ***/ /************************/ /** @dev Returns if a default can be triggered. @param nextPaymentDue Timestamp of when payment is due. @param defaultGracePeriod Amount of time after the next payment is due that a Borrower has before a liquidation can occur. @param superFactory Factory that instantiated Loan. @param balance LoanFDT balance of account trying to trigger a default. @param totalSupply Total supply of LoanFDT. @return Boolean indicating if default can be triggered. */ function canTriggerDefault(uint256 nextPaymentDue, uint256 defaultGracePeriod, address superFactory, uint256 balance, uint256 totalSupply) external view returns (bool) { bool pastDefaultGracePeriod = block.timestamp > nextPaymentDue.add(defaultGracePeriod); // Check if the Loan is past the default grace period and that the account triggering the default has a percentage of total LoanFDTs // that is greater than the minimum equity needed (specified in globals) return pastDefaultGracePeriod && balance >= ((totalSupply * _globals(superFactory).minLoanEquity()) / 10_000); } /** @dev Returns information on next payment amount. @param repaymentCalc Address of RepaymentCalc. @param nextPaymentDue Timestamp of when payment is due. @param lateFeeCalc Address of LateFeeCalc. @return total Entitled total amount needed to be paid in the next payment (Principal + Interest only when the next payment is last payment of the Loan). @return principal Entitled principal amount needed to be paid in the next payment. @return interest Entitled interest amount needed to be paid in the next payment. @return _nextPaymentDue Payment Due Date. @return paymentLate Whether payment is late. */ function getNextPayment( address repaymentCalc, uint256 nextPaymentDue, address lateFeeCalc ) external view returns ( uint256 total, uint256 principal, uint256 interest, uint256 _nextPaymentDue, bool paymentLate ) { _nextPaymentDue = nextPaymentDue; // Get next payment amounts from RepaymentCalc. (total, principal, interest) = IRepaymentCalc(repaymentCalc).getNextPayment(address(this)); paymentLate = block.timestamp > _nextPaymentDue; // If payment is late, add late fees. if (paymentLate) { uint256 lateFee = ILateFeeCalc(lateFeeCalc).getLateFee(interest); total = total.add(lateFee); interest = interest.add(lateFee); } } /** @dev Returns information on full payment amount. @param repaymentCalc Address of RepaymentCalc. @param nextPaymentDue Timestamp of when payment is due. @param lateFeeCalc Address of LateFeeCalc. @param premiumCalc Address of PremiumCalc. @return total Principal + Interest for the full payment. @return principal Entitled principal amount needed to be paid in the full payment. @return interest Entitled interest amount needed to be paid in the full payment. */ function getFullPayment( address repaymentCalc, uint256 nextPaymentDue, address lateFeeCalc, address premiumCalc ) external view returns ( uint256 total, uint256 principal, uint256 interest ) { (total, principal, interest) = IPremiumCalc(premiumCalc).getPremiumPayment(address(this)); if (block.timestamp <= nextPaymentDue) return (total, principal, interest); // If payment is late, calculate and add late fees using interest amount from regular payment. (,, uint256 regInterest) = IRepaymentCalc(repaymentCalc).getNextPayment(address(this)); uint256 lateFee = ILateFeeCalc(lateFeeCalc).getLateFee(regInterest); total = total.add(lateFee); interest = interest.add(lateFee); } /** @dev Calculates collateral required to drawdown amount. @param collateralAsset IERC20 of the Collateral Asset. @param liquidityAsset IERC20 of the Liquidity Asset. @param collateralRatio Percentage of drawdown value that must be posted as collateral. @param superFactory Factory that instantiated Loan. @param amt Drawdown amount. @return Amount of Collateral Asset required to post in CollateralLocker for given drawdown amount. */ function collateralRequiredForDrawdown( IERC20Details collateralAsset, IERC20Details liquidityAsset, uint256 collateralRatio, address superFactory, uint256 amt ) external view returns (uint256) { IMapleGlobals globals = _globals(superFactory); uint256 wad = _toWad(amt, liquidityAsset); // Convert to WAD precision. // Fetch current value of Liquidity Asset and Collateral Asset (Chainlink oracles provide 8 decimal precision). uint256 liquidityAssetPrice = globals.getLatestPrice(address(liquidityAsset)); uint256 collateralPrice = globals.getLatestPrice(address(collateralAsset)); // Calculate collateral required. uint256 collateralRequiredUSD = wad.mul(liquidityAssetPrice).mul(collateralRatio).div(10_000); // 18 + 8 = 26 decimals uint256 collateralRequiredWAD = collateralRequiredUSD.div(collateralPrice); // 26 - 8 = 18 decimals return collateralRequiredWAD.mul(10 ** collateralAsset.decimals()).div(10 ** 18); // 18 + collateralAssetDecimals - 18 = collateralAssetDecimals } /************************/ /*** Helper Functions ***/ /************************/ function _globals(address loanFactory) internal view returns (IMapleGlobals) { return IMapleGlobals(ILoanFactory(loanFactory).globals()); } function _toWad(uint256 amt, IERC20Details liquidityAsset) internal view returns (uint256) { return amt.mul(10 ** 18).div(10 ** liquidityAsset.decimals()); } } ////// contracts/math/SafeMathInt.sol /* pragma solidity 0.6.11; */ library SafeMathInt { function toUint256Safe(int256 a) internal pure returns (uint256) { require(a >= 0, "SMI:NEG"); return uint256(a); } } ////// contracts/math/SafeMathUint.sol /* pragma solidity 0.6.11; */ library SafeMathUint { function toInt256Safe(uint256 a) internal pure returns (int256 b) { b = int256(a); require(b >= 0, "SMU:OOB"); } } ////// lib/openzeppelin-contracts/contracts/math/SignedSafeMath.sol /* pragma solidity >=0.6.0 <0.8.0; */ /** * @title SignedSafeMath * @dev Signed math operations with safety checks that revert on error. */ library SignedSafeMath { int256 constant private _INT256_MIN = -2**255; /** * @dev Returns the multiplication of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(int256 a, int256 b) internal pure returns (int256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow"); int256 c = a * b; require(c / a == b, "SignedSafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two signed integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(int256 a, int256 b) internal pure returns (int256) { require(b != 0, "SignedSafeMath: division by zero"); require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow"); int256 c = a / b; return c; } /** * @dev Returns the subtraction of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(int256 a, int256 b) internal pure returns (int256) { int256 c = a - b; require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); return c; } /** * @dev Returns the addition of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(int256 a, int256 b) internal pure returns (int256) { int256 c = a + b; require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); return c; } } ////// lib/openzeppelin-contracts/contracts/GSN/Context.sol /* pragma solidity >=0.6.0 <0.8.0; */ /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } ////// lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol /* pragma solidity >=0.6.0 <0.8.0; */ /* import "../../GSN/Context.sol"; */ /* import "./IERC20.sol"; */ /* import "../../math/SafeMath.sol"; */ /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } ////// contracts/token/BasicFDT.sol /* pragma solidity 0.6.11; */ /* import "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; */ /* import "lib/openzeppelin-contracts/contracts/math/SafeMath.sol"; */ /* import "lib/openzeppelin-contracts/contracts/math/SignedSafeMath.sol"; */ /* import "./interfaces/IBaseFDT.sol"; */ /* import "../math/SafeMathUint.sol"; */ /* import "../math/SafeMathInt.sol"; */ /// @title BasicFDT implements base level FDT functionality for accounting for revenues. abstract contract BasicFDT is IBaseFDT, ERC20 { using SafeMath for uint256; using SafeMathUint for uint256; using SignedSafeMath for int256; using SafeMathInt for int256; uint256 internal constant pointsMultiplier = 2 ** 128; uint256 internal pointsPerShare; mapping(address => int256) internal pointsCorrection; mapping(address => uint256) internal withdrawnFunds; event PointsPerShareUpdated(uint256 pointsPerShare); event PointsCorrectionUpdated(address indexed account, int256 pointsCorrection); constructor(string memory name, string memory symbol) ERC20(name, symbol) public { } /** @dev Distributes funds to token holders. @dev It reverts if the total supply of tokens is 0. @dev It emits a `FundsDistributed` event if the amount of received funds is greater than 0. @dev It emits a `PointsPerShareUpdated` event if the amount of received funds is greater than 0. About undistributed funds: In each distribution, there is a small amount of funds which do not get distributed, which is `(value pointsMultiplier) % totalSupply()`. With a well-chosen `pointsMultiplier`, the amount funds that are not getting distributed in a distribution can be less than 1 (base unit). We can actually keep track of the undistributed funds in a distribution and try to distribute it in the next distribution. */ function _distributeFunds(uint256 value) internal { require(totalSupply() > 0, "FDT:ZERO_SUPPLY"); if (value == 0) return; pointsPerShare = pointsPerShare.add(value.mul(pointsMultiplier) / totalSupply()); emit FundsDistributed(msg.sender, value); emit PointsPerShareUpdated(pointsPerShare); } /** @dev Prepares the withdrawal of funds. @dev It emits a `FundsWithdrawn` event if the amount of withdrawn funds is greater than 0. @return withdrawableDividend The amount of dividend funds that can be withdrawn. */ function _prepareWithdraw() internal returns (uint256 withdrawableDividend) { withdrawableDividend = withdrawableFundsOf(msg.sender); uint256 _withdrawnFunds = withdrawnFunds[msg.sender].add(withdrawableDividend); withdrawnFunds[msg.sender] = _withdrawnFunds; emit FundsWithdrawn(msg.sender, withdrawableDividend, _withdrawnFunds); } /** @dev Returns the amount of funds that an account can withdraw. @param _owner The address of a token holder. @return The amount funds that `_owner` can withdraw. */ function withdrawableFundsOf(address _owner) public view override returns (uint256) { return accumulativeFundsOf(_owner).sub(withdrawnFunds[_owner]); } /** @dev Returns the amount of funds that an account has withdrawn. @param _owner The address of a token holder. @return The amount of funds that `_owner` has withdrawn. */ function withdrawnFundsOf(address _owner) external view returns (uint256) { return withdrawnFunds[_owner]; } /** @dev Returns the amount of funds that an account has earned in total. @dev accumulativeFundsOf(_owner) = withdrawableFundsOf(_owner) + withdrawnFundsOf(_owner) = (pointsPerShare * balanceOf(_owner) + pointsCorrection[_owner]) / pointsMultiplier @param _owner The address of a token holder. @return The amount of funds that `_owner` has earned in total. */ function accumulativeFundsOf(address _owner) public view returns (uint256) { return pointsPerShare .mul(balanceOf(_owner)) .toInt256Safe() .add(pointsCorrection[_owner]) .toUint256Safe() / pointsMultiplier; } /** @dev Transfers tokens from one account to another. Updates pointsCorrection to keep funds unchanged. @dev It emits two `PointsCorrectionUpdated` events, one for the sender and one for the receiver. @param from The address to transfer from. @param to The address to transfer to. @param value The amount to be transferred. */ function _transfer( address from, address to, uint256 value ) internal virtual override { super._transfer(from, to, value); int256 _magCorrection = pointsPerShare.mul(value).toInt256Safe(); int256 pointsCorrectionFrom = pointsCorrection[from].add(_magCorrection); pointsCorrection[from] = pointsCorrectionFrom; int256 pointsCorrectionTo = pointsCorrection[to].sub(_magCorrection); pointsCorrection[to] = pointsCorrectionTo; emit PointsCorrectionUpdated(from, pointsCorrectionFrom); emit PointsCorrectionUpdated(to, pointsCorrectionTo); } /** @dev Mints tokens to an account. Updates pointsCorrection to keep funds unchanged. @param account The account that will receive the created tokens. @param value The amount that will be created. */ function _mint(address account, uint256 value) internal virtual override { super._mint(account, value); int256 _pointsCorrection = pointsCorrection[account].sub( (pointsPerShare.mul(value)).toInt256Safe() ); pointsCorrection[account] = _pointsCorrection; emit PointsCorrectionUpdated(account, _pointsCorrection); } /** @dev Burns an amount of the token of a given account. Updates pointsCorrection to keep funds unchanged. @dev It emits a `PointsCorrectionUpdated` event. @param account The account whose tokens will be burnt. @param value The amount that will be burnt. */ function _burn(address account, uint256 value) internal virtual override { super._burn(account, value); int256 _pointsCorrection = pointsCorrection[account].add( (pointsPerShare.mul(value)).toInt256Safe() ); pointsCorrection[account] = _pointsCorrection; emit PointsCorrectionUpdated(account, _pointsCorrection); } /** @dev Withdraws all available funds for a token holder. */ function withdrawFunds() public virtual override {} /** @dev Updates the current `fundsToken` balance and returns the difference of the new and previous `fundsToken` balance. @return A int256 representing the difference of the new and previous `fundsToken` balance. */ function _updateFundsTokenBalance() internal virtual returns (int256) {} /** @dev Registers a payment of funds in tokens. May be called directly after a deposit is made. @dev Calls _updateFundsTokenBalance(), whereby the contract computes the delta of the new and previous `fundsToken` balance and increments the total received funds (cumulative), by delta, by calling _distributeFunds(). */ function updateFundsReceived() public virtual { int256 newFunds = _updateFundsTokenBalance(); if (newFunds <= 0) return; _distributeFunds(newFunds.toUint256Safe()); } } ////// contracts/token/LoanFDT.sol /* pragma solidity 0.6.11; */ /* import "lib/openzeppelin-contracts/contracts/token/ERC20/SafeERC20.sol"; */ /* import "./BasicFDT.sol"; */ /// @title LoanFDT inherits BasicFDT and uses the original ERC-2222 logic. abstract contract LoanFDT is BasicFDT { using SafeMath for uint256; using SafeMathUint for uint256; using SignedSafeMath for int256; using SafeMathInt for int256; using SafeERC20 for IERC20; IERC20 public immutable fundsToken; // The `fundsToken` (dividends). uint256 public fundsTokenBalance; // The amount of `fundsToken` (Liquidity Asset) currently present and accounted for in this contract. constructor(string memory name, string memory symbol, address _fundsToken) BasicFDT(name, symbol) public { fundsToken = IERC20(_fundsToken); } /** @dev Withdraws all available funds for a token holder. */ function withdrawFunds() public virtual override { uint256 withdrawableFunds = _prepareWithdraw(); if (withdrawableFunds > uint256(0)) { fundsToken.safeTransfer(msg.sender, withdrawableFunds); _updateFundsTokenBalance(); } } /** @dev Updates the current `fundsToken` balance and returns the difference of the new and previous `fundsToken` balance. @return A int256 representing the difference of the new and previous `fundsToken` balance. */ function _updateFundsTokenBalance() internal virtual override returns (int256) { uint256 _prevFundsTokenBalance = fundsTokenBalance; fundsTokenBalance = fundsToken.balanceOf(address(this)); return int256(fundsTokenBalance).sub(int256(_prevFundsTokenBalance)); } } ////// lib/openzeppelin-contracts/contracts/utils/Pausable.sol /* pragma solidity >=0.6.0 <0.8.0; */ /* import "../GSN/Context.sol"; */ /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor () internal { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!_paused, "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(_paused, "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } } ////// contracts/Loan.sol /* pragma solidity 0.6.11; */ /* import "lib/openzeppelin-contracts/contracts/utils/Pausable.sol"; */ /* import "lib/openzeppelin-contracts/contracts/token/ERC20/SafeERC20.sol"; */ /* import "./interfaces/ICollateralLocker.sol"; */ /* import "./interfaces/ICollateralLockerFactory.sol"; */ /* import "./interfaces/IERC20Details.sol"; */ /* import "./interfaces/IFundingLocker.sol"; */ /* import "./interfaces/IFundingLockerFactory.sol"; */ /* import "./interfaces/IMapleGlobals.sol"; */ /* import "./interfaces/ILateFeeCalc.sol"; */ /* import "./interfaces/ILiquidityLocker.sol"; */ /* import "./interfaces/ILoanFactory.sol"; */ /* import "./interfaces/IPool.sol"; */ /* import "./interfaces/IPoolFactory.sol"; */ /* import "./interfaces/IPremiumCalc.sol"; */ /* import "./interfaces/IRepaymentCalc.sol"; */ /* import "./interfaces/IUniswapRouter.sol"; */ /* import "./library/Util.sol"; */ /* import "./library/LoanLib.sol"; */ /* import "./token/LoanFDT.sol"; */ /// @title Loan maintains all accounting and functionality related to Loans. contract Loan is LoanFDT, Pausable { using SafeMathInt for int256; using SignedSafeMath for int256; using SafeMath for uint256; using SafeERC20 for IERC20; /** Ready = The Loan has been initialized and is ready for funding (assuming funding period hasn't ended) Active = The Loan has been drawdown and the Borrower is making payments Matured = The Loan is fully paid off and has "matured" Expired = The Loan did not initiate, and all funding was returned to Lenders Liquidated = The Loan has been liquidated */ enum State { Ready, Active, Matured, Expired, Liquidated } State public loanState; // The current state of this Loan, as defined in the State enum below. IERC20 public immutable liquidityAsset; // The asset deposited by Lenders into the FundingLocker, when funding this Loan. IERC20 public immutable collateralAsset; // The asset deposited by Borrower into the CollateralLocker, for collateralizing this Loan. address public immutable fundingLocker; // The FundingLocker that holds custody of Loan funds before drawdown. address public immutable flFactory; // The FundingLockerFactory. address public immutable collateralLocker; // The CollateralLocker that holds custody of Loan collateral. address public immutable clFactory; // The CollateralLockerFactory. address public immutable borrower; // The Borrower of this Loan, responsible for repayments. address public immutable repaymentCalc; // The RepaymentCalc for this Loan. address public immutable lateFeeCalc; // The LateFeeCalc for this Loan. address public immutable premiumCalc; // The PremiumCalc for this Loan. address public immutable superFactory; // The LoanFactory that deployed this Loan. mapping(address => bool) public loanAdmins; // Admin addresses that have permission to do certain operations in case of disaster management. uint256 public nextPaymentDue; // The unix timestamp due date of the next payment. // Loan specifications uint256 public immutable apr; // The APR in basis points. uint256 public paymentsRemaining; // The number of payments remaining on the Loan. uint256 public immutable termDays; // The total length of the Loan term in days. uint256 public immutable paymentIntervalSeconds; // The time between Loan payments in seconds. uint256 public immutable requestAmount; // The total requested amount for Loan. uint256 public immutable collateralRatio; // The percentage of value of the drawdown amount to post as collateral in basis points. uint256 public immutable createdAt; // The timestamp of when Loan was instantiated. uint256 public immutable fundingPeriod; // The time for a Loan to be funded in seconds. uint256 public immutable defaultGracePeriod; // The time a Borrower has, after a payment is due, to make a payment before a liquidation can occur. // Accounting variables uint256 public principalOwed; // The amount of principal owed (initially the drawdown amount). uint256 public principalPaid; // The amount of principal that has been paid by the Borrower since the Loan instantiation. uint256 public interestPaid; // The amount of interest that has been paid by the Borrower since the Loan instantiation. uint256 public feePaid; // The amount of fees that have been paid by the Borrower since the Loan instantiation. uint256 public excessReturned; // The amount of excess that has been returned to the Lenders after the Loan drawdown. // Liquidation variables uint256 public amountLiquidated; // The amount of Collateral Asset that has been liquidated after default. uint256 public amountRecovered; // The amount of Liquidity Asset that has been recovered after default. uint256 public defaultSuffered; // The difference between `amountRecovered` and `principalOwed` after liquidation. uint256 public liquidationExcess; // If `amountRecovered > principalOwed`, this is the amount of Liquidity Asset that is to be returned to the Borrower. event LoanFunded(address indexed fundedBy, uint256 amountFunded); event BalanceUpdated(address indexed account, address indexed token, uint256 balance); event Drawdown(uint256 drawdownAmount); event LoanStateChanged(State state); event LoanAdminSet(address indexed loanAdmin, bool allowed); event PaymentMade( uint256 totalPaid, uint256 principalPaid, uint256 interestPaid, uint256 paymentsRemaining, uint256 principalOwed, uint256 nextPaymentDue, bool latePayment ); event Liquidation( uint256 collateralSwapped, uint256 liquidityAssetReturned, uint256 liquidationExcess, uint256 defaultSuffered ); /** @dev Constructor for a Loan. @dev It emits a `LoanStateChanged` event. @param _borrower Will receive the funding when calling `drawdown()`. Is also responsible for repayments. @param _liquidityAsset The asset the Borrower is requesting funding in. @param _collateralAsset The asset provided as collateral by the Borrower. @param _flFactory Factory to instantiate FundingLocker with. @param _clFactory Factory to instantiate CollateralLocker with. @param specs Contains specifications for this Loan. specs[0] = apr specs[1] = termDays specs[2] = paymentIntervalDays (aka PID) specs[3] = requestAmount specs[4] = collateralRatio @param calcs The calculators used for this Loan. calcs[0] = repaymentCalc calcs[1] = lateFeeCalc calcs[2] = premiumCalc */ constructor( address _borrower, address _liquidityAsset, address _collateralAsset, address _flFactory, address _clFactory, uint256[5] memory specs, address[3] memory calcs ) LoanFDT("Maple Loan Token", "MPL-LOAN", _liquidityAsset) public { IMapleGlobals globals = _globals(msg.sender); // Perform validity cross-checks. LoanLib.loanSanityChecks(globals, _liquidityAsset, _collateralAsset, specs); borrower = _borrower; liquidityAsset = IERC20(_liquidityAsset); collateralAsset = IERC20(_collateralAsset); flFactory = _flFactory; clFactory = _clFactory; createdAt = block.timestamp; // Update state variables. apr = specs[0]; termDays = specs[1]; paymentsRemaining = specs[1].div(specs[2]); paymentIntervalSeconds = specs[2].mul(1 days); requestAmount = specs[3]; collateralRatio = specs[4]; fundingPeriod = globals.fundingPeriod(); defaultGracePeriod = globals.defaultGracePeriod(); repaymentCalc = calcs[0]; lateFeeCalc = calcs[1]; premiumCalc = calcs[2]; superFactory = msg.sender; // Deploy lockers. collateralLocker = ICollateralLockerFactory(_clFactory).newLocker(_collateralAsset); fundingLocker = IFundingLockerFactory(_flFactory).newLocker(_liquidityAsset); emit LoanStateChanged(State.Ready); } /**************************/ /*** Borrower Functions ***/ /**************************/ /** @dev Draws down funding from FundingLocker, posts collateral, and transitions the Loan state from `Ready` to `Active`. Only the Borrower can call this function. @dev It emits four `BalanceUpdated` events. @dev It emits a `LoanStateChanged` event. @dev It emits a `Drawdown` event. @param amt Amount of Liquidity Asset the Borrower draws down. Remainder is returned to the Loan where it can be claimed back by LoanFDT holders. */ function drawdown(uint256 amt) external { _whenProtocolNotPaused(); _isValidBorrower(); _isValidState(State.Ready); IMapleGlobals globals = _globals(superFactory); IFundingLocker _fundingLocker = IFundingLocker(fundingLocker); require(amt >= requestAmount, "L:AMT_LT_REQUEST_AMT"); require(amt <= _getFundingLockerBalance(), "L:AMT_GT_FUNDED_AMT"); // Update accounting variables for the Loan. principalOwed = amt; nextPaymentDue = block.timestamp.add(paymentIntervalSeconds); loanState = State.Active; // Transfer the required amount of collateral for drawdown from the Borrower to the CollateralLocker. collateralAsset.safeTransferFrom(borrower, collateralLocker, collateralRequiredForDrawdown(amt)); // Transfer funding amount from the FundingLocker to the Borrower, then drain remaining funds to the Loan. uint256 treasuryFee = globals.treasuryFee(); uint256 investorFee = globals.investorFee(); address treasury = globals.mapleTreasury(); uint256 _feePaid = feePaid = amt.mul(investorFee).div(10_000); // Update fees paid for `claim()`. uint256 treasuryAmt = amt.mul(treasuryFee).div(10_000); // Calculate amount to send to the MapleTreasury. _transferFunds(_fundingLocker, treasury, treasuryAmt); // Send the treasury fee directly to the MapleTreasury. _transferFunds(_fundingLocker, borrower, amt.sub(treasuryAmt).sub(_feePaid)); // Transfer drawdown amount to the Borrower. // Update excessReturned for `claim()`. excessReturned = _getFundingLockerBalance().sub(_feePaid); // Drain remaining funds from the FundingLocker (amount equal to `excessReturned` plus `feePaid`) _fundingLocker.drain(); // Call `updateFundsReceived()` update LoanFDT accounting with funds received from fees and excess returned. updateFundsReceived(); _emitBalanceUpdateEventForCollateralLocker(); _emitBalanceUpdateEventForFundingLocker(); _emitBalanceUpdateEventForLoan(); emit BalanceUpdated(treasury, address(liquidityAsset), liquidityAsset.balanceOf(treasury)); emit LoanStateChanged(State.Active); emit Drawdown(amt); } /** @dev Makes a payment for this Loan. Amounts are calculated for the Borrower. */ function makePayment() external { _whenProtocolNotPaused(); _isValidState(State.Active); (uint256 total, uint256 principal, uint256 interest,, bool paymentLate) = getNextPayment(); --paymentsRemaining; _makePayment(total, principal, interest, paymentLate); } /** @dev Makes the full payment for this Loan (a.k.a. "calling" the Loan). This requires the Borrower to pay a premium fee. */ function makeFullPayment() external { _whenProtocolNotPaused(); _isValidState(State.Active); (uint256 total, uint256 principal, uint256 interest) = getFullPayment(); paymentsRemaining = uint256(0); _makePayment(total, principal, interest, false); } /** @dev Updates the payment variables and transfers funds from the Borrower into the Loan. @dev It emits one or two `BalanceUpdated` events (depending if payments remaining). @dev It emits a `LoanStateChanged` event if no payments remaining. @dev It emits a `PaymentMade` event. */ function _makePayment(uint256 total, uint256 principal, uint256 interest, bool paymentLate) internal { // Caching to reduce `SLOADs`. uint256 _paymentsRemaining = paymentsRemaining; // Update internal accounting variables. interestPaid = interestPaid.add(interest); if (principal > uint256(0)) principalPaid = principalPaid.add(principal); if (_paymentsRemaining > uint256(0)) { // Update info related to next payment and, if needed, decrement principalOwed. nextPaymentDue = nextPaymentDue.add(paymentIntervalSeconds); if (principal > uint256(0)) principalOwed = principalOwed.sub(principal); } else { // Update info to close loan. principalOwed = uint256(0); loanState = State.Matured; nextPaymentDue = uint256(0); // Transfer all collateral back to the Borrower. ICollateralLocker(collateralLocker).pull(borrower, _getCollateralLockerBalance()); _emitBalanceUpdateEventForCollateralLocker(); emit LoanStateChanged(State.Matured); } // Loan payer sends funds to the Loan. liquidityAsset.safeTransferFrom(msg.sender, address(this), total); // Update FDT accounting with funds received from interest payment. updateFundsReceived(); emit PaymentMade( total, principal, interest, _paymentsRemaining, principalOwed, _paymentsRemaining > 0 ? nextPaymentDue : 0, paymentLate ); _emitBalanceUpdateEventForLoan(); } /************************/ /*** Lender Functions ***/ /************************/ /** @dev Funds this Loan and mints LoanFDTs for `mintTo` (DebtLocker in the case of Pool funding). Only LiquidityLocker using valid/approved Pool can call this function. @dev It emits a `LoanFunded` event. @dev It emits a `BalanceUpdated` event. @param amt Amount to fund the Loan. @param mintTo Address that LoanFDTs are minted to. */ function fundLoan(address mintTo, uint256 amt) whenNotPaused external { _whenProtocolNotPaused(); _isValidState(State.Ready); _isValidPool(); _isWithinFundingPeriod(); liquidityAsset.safeTransferFrom(msg.sender, fundingLocker, amt); uint256 wad = _toWad(amt); // Convert to WAD precision. _mint(mintTo, wad); // Mint LoanFDTs to `mintTo` (i.e DebtLocker contract). emit LoanFunded(mintTo, amt); _emitBalanceUpdateEventForFundingLocker(); } /** @dev Handles returning capital to the Loan, where it can be claimed back by LoanFDT holders, if the Borrower has not drawn down on the Loan past the drawdown grace period. @dev It emits a `LoanStateChanged` event. */ function unwind() external { _whenProtocolNotPaused(); _isValidState(State.Ready); // Update accounting for `claim()` and transfer funds from FundingLocker to Loan. excessReturned = LoanLib.unwind(liquidityAsset, fundingLocker, createdAt, fundingPeriod); updateFundsReceived(); // Transition state to `Expired`. loanState = State.Expired; emit LoanStateChanged(State.Expired); } /** @dev Triggers a default if the Loan meets certain default conditions, liquidating all collateral and updating accounting. Only the an account with sufficient LoanFDTs of this Loan can call this function. @dev It emits a `BalanceUpdated` event. @dev It emits a `Liquidation` event. @dev It emits a `LoanStateChanged` event. */ function triggerDefault() external { _whenProtocolNotPaused(); _isValidState(State.Active); require(LoanLib.canTriggerDefault(nextPaymentDue, defaultGracePeriod, superFactory, balanceOf(msg.sender), totalSupply()), "L:FAILED_TO_LIQ"); // Pull the Collateral Asset from the CollateralLocker, swap to the Liquidity Asset, and hold custody of the resulting Liquidity Asset in the Loan. (amountLiquidated, amountRecovered) = LoanLib.liquidateCollateral(collateralAsset, address(liquidityAsset), superFactory, collateralLocker); _emitBalanceUpdateEventForCollateralLocker(); // Decrement `principalOwed` by `amountRecovered`, set `defaultSuffered` to the difference (shortfall from the liquidation). if (amountRecovered <= principalOwed) { principalOwed = principalOwed.sub(amountRecovered); defaultSuffered = principalOwed; } // Set `principalOwed` to zero and return excess value from the liquidation back to the Borrower. else { liquidationExcess = amountRecovered.sub(principalOwed); principalOwed = 0; liquidityAsset.safeTransfer(borrower, liquidationExcess); // Send excess to the Borrower. } // Update LoanFDT accounting with funds received from the liquidation. updateFundsReceived(); // Transition `loanState` to `Liquidated` loanState = State.Liquidated; emit Liquidation( amountLiquidated, // Amount of Collateral Asset swapped. amountRecovered, // Amount of Liquidity Asset recovered from swap. liquidationExcess, // Amount of Liquidity Asset returned to borrower. defaultSuffered // Remaining losses after the liquidation. ); emit LoanStateChanged(State.Liquidated); } /***********************/ /*** Admin Functions ***/ /***********************/ /** @dev Triggers paused state. Halts functionality for certain functions. Only the Borrower or a Loan Admin can call this function. */ function pause() external { _isValidBorrowerOrLoanAdmin(); super._pause(); } /** @dev Triggers unpaused state. Restores functionality for certain functions. Only the Borrower or a Loan Admin can call this function. */ function unpause() external { _isValidBorrowerOrLoanAdmin(); super._unpause(); } /** @dev Sets a Loan Admin. Only the Borrower can call this function. @dev It emits a `LoanAdminSet` event. @param loanAdmin An address being allowed or disallowed as a Loan Admin. @param allowed Status of a Loan Admin. */ function setLoanAdmin(address loanAdmin, bool allowed) external { _whenProtocolNotPaused(); _isValidBorrower(); loanAdmins[loanAdmin] = allowed; emit LoanAdminSet(loanAdmin, allowed); } /**************************/ /*** Governor Functions ***/ /**************************/ /** @dev Transfers any locked funds to the Governor. Only the Governor can call this function. @param token Address of the token to be reclaimed. */ function reclaimERC20(address token) external { LoanLib.reclaimERC20(token, address(liquidityAsset), _globals(superFactory)); } /*********************/ /*** FDT Functions ***/ /*********************/ /** @dev Withdraws all available funds earned through LoanFDT for a token holder. @dev It emits a `BalanceUpdated` event. */ function withdrawFunds() public override { _whenProtocolNotPaused(); super.withdrawFunds(); emit BalanceUpdated(address(this), address(fundsToken), fundsToken.balanceOf(address(this))); } /************************/ /*** Getter Functions ***/ /************************/ /** @dev Returns the expected amount of Liquidity Asset to be recovered from a liquidation based on current oracle prices. @return The minimum amount of Liquidity Asset that can be expected by swapping Collateral Asset. */ function getExpectedAmountRecovered() external view returns (uint256) { uint256 liquidationAmt = _getCollateralLockerBalance(); return Util.calcMinAmount(_globals(superFactory), address(collateralAsset), address(liquidityAsset), liquidationAmt); } /** @dev Returns information of the next payment amount. @return [0] = Entitled interest of the next payment (Principal + Interest only when the next payment is last payment of the Loan) [1] = Entitled principal amount needed to be paid in the next payment [2] = Entitled interest amount needed to be paid in the next payment [3] = Payment Due Date [4] = Is Payment Late */ function getNextPayment() public view returns (uint256, uint256, uint256, uint256, bool) { return LoanLib.getNextPayment(repaymentCalc, nextPaymentDue, lateFeeCalc); } /** @dev Returns the information of a full payment amount. @return total Principal and interest owed, combined. @return principal Principal owed. @return interest Interest owed. */ function getFullPayment() public view returns (uint256 total, uint256 principal, uint256 interest) { (total, principal, interest) = LoanLib.getFullPayment(repaymentCalc, nextPaymentDue, lateFeeCalc, premiumCalc); } /** @dev Calculates the collateral required to draw down amount. @param amt The amount of the Liquidity Asset to draw down from the FundingLocker. @return The amount of the Collateral Asset required to post in the CollateralLocker for a given drawdown amount. */ function collateralRequiredForDrawdown(uint256 amt) public view returns (uint256) { return LoanLib.collateralRequiredForDrawdown( IERC20Details(address(collateralAsset)), IERC20Details(address(liquidityAsset)), collateralRatio, superFactory, amt ); } /************************/ /*** Helper Functions ***/ /************************/ /** @dev Checks that the protocol is not in a paused state. */ function _whenProtocolNotPaused() internal view { require(!_globals(superFactory).protocolPaused(), "L:PROTO_PAUSED"); } /** @dev Checks that `msg.sender` is the Borrower or a Loan Admin. */ function _isValidBorrowerOrLoanAdmin() internal view { require(msg.sender == borrower || loanAdmins[msg.sender], "L:NOT_BORROWER_OR_ADMIN"); } /** @dev Converts to WAD precision. */ function _toWad(uint256 amt) internal view returns (uint256) { return amt.mul(10 ** 18).div(10 ** IERC20Details(address(liquidityAsset)).decimals()); } /** @dev Returns the MapleGlobals instance. */ function _globals(address loanFactory) internal view returns (IMapleGlobals) { return IMapleGlobals(ILoanFactory(loanFactory).globals()); } /** @dev Returns the CollateralLocker balance. */ function _getCollateralLockerBalance() internal view returns (uint256) { return collateralAsset.balanceOf(collateralLocker); } /** @dev Returns the FundingLocker balance. */ function _getFundingLockerBalance() internal view returns (uint256) { return liquidityAsset.balanceOf(fundingLocker); } /** @dev Checks that the current state of the Loan matches the provided state. @param _state Enum of desired Loan state. */ function _isValidState(State _state) internal view { require(loanState == _state, "L:INVALID_STATE"); } /** @dev Checks that `msg.sender` is the Borrower. */ function _isValidBorrower() internal view { require(msg.sender == borrower, "L:NOT_BORROWER"); } /** @dev Checks that `msg.sender` is a Lender (LiquidityLocker) that is using an approved Pool to fund the Loan. */ function _isValidPool() internal view { address pool = ILiquidityLocker(msg.sender).pool(); address poolFactory = IPool(pool).superFactory(); require( _globals(superFactory).isValidPoolFactory(poolFactory) && IPoolFactory(poolFactory).isPool(pool), "L:INVALID_LENDER" ); } /** @dev Checks that "now" is currently within the funding period. */ function _isWithinFundingPeriod() internal view { require(block.timestamp <= createdAt.add(fundingPeriod), "L:PAST_FUNDING_PERIOD"); } /** @dev Transfers funds from the FundingLocker. @param from Instance of the FundingLocker. @param to Address to send funds to. @param value Amount to send. */ function _transferFunds(IFundingLocker from, address to, uint256 value) internal { from.pull(to, value); } /** @dev Emits a `BalanceUpdated` event for the Loan. @dev It emits a `BalanceUpdated` event. */ function _emitBalanceUpdateEventForLoan() internal { emit BalanceUpdated(address(this), address(liquidityAsset), liquidityAsset.balanceOf(address(this))); } /** @dev Emits a `BalanceUpdated` event for the FundingLocker. @dev It emits a `BalanceUpdated` event. */ function _emitBalanceUpdateEventForFundingLocker() internal { emit BalanceUpdated(fundingLocker, address(liquidityAsset), _getFundingLockerBalance()); } /** @dev Emits a `BalanceUpdated` event for the CollateralLocker. @dev It emits a `BalanceUpdated` event. */ function _emitBalanceUpdateEventForCollateralLocker() internal { emit BalanceUpdated(collateralLocker, address(collateralAsset), _getCollateralLockerBalance()); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"address","name":"_liquidityAsset","type":"address"},{"internalType":"address","name":"_collateralAsset","type":"address"},{"internalType":"address","name":"_flFactory","type":"address"},{"internalType":"address","name":"_clFactory","type":"address"},{"internalType":"uint256[5]","name":"specs","type":"uint256[5]"},{"internalType":"address[3]","name":"calcs","type":"address[3]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"balance","type":"uint256"}],"name":"BalanceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"drawdownAmount","type":"uint256"}],"name":"Drawdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"fundsDistributed","type":"uint256"}],"name":"FundsDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"fundsWithdrawn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalWithdrawn","type":"uint256"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"collateralSwapped","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidityAssetReturned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidationExcess","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"defaultSuffered","type":"uint256"}],"name":"Liquidation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"loanAdmin","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"LoanAdminSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fundedBy","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountFunded","type":"uint256"}],"name":"LoanFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum Loan.State","name":"state","type":"uint8"}],"name":"LoanStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalPaid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"principalPaid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestPaid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paymentsRemaining","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"principalOwed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nextPaymentDue","type":"uint256"},{"indexed":false,"internalType":"bool","name":"latePayment","type":"bool"}],"name":"PaymentMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"int256","name":"pointsCorrection","type":"int256"}],"name":"PointsCorrectionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pointsPerShare","type":"uint256"}],"name":"PointsPerShareUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"accumulativeFundsOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amountLiquidated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amountRecovered","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"apr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrower","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralAsset","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralLocker","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"collateralRequiredForDrawdown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"createdAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultGracePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultSuffered","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"drawdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"excessReturned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feePaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"mintTo","type":"address"},{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"fundLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fundingLocker","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundingPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundsToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundsTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExpectedAmountRecovered","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFullPayment","outputs":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNextPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lateFeeCalc","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidationExcess","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityAsset","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"loanAdmins","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"loanState","outputs":[{"internalType":"enum Loan.State","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"makeFullPayment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"makePayment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextPaymentDue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentIntervalSeconds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentsRemaining","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"premiumCalc","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"principalOwed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"principalPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"reclaimERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repaymentCalc","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"requestAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"loanAdmin","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setLoanAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"superFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"termDays","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"triggerDefault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unwind","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateFundsReceived","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"withdrawableFundsOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"withdrawnFundsOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6103006040523480156200001257600080fd5b5060405162004a8d38038062004a8d83398181016040526101a08110156200003957600080fd5b5080516020808301516040808501516060860151608087015183518085018552601081526f26b0b83632902637b0b7102a37b5b2b760811b8188019081528551808701909652600886526726a82616a627a0a760c11b9786019790975280519798959793969295919460a0830194610140909301939192909189918491849183918391620000ca91600391620006bf565b508051620000e0906004906020840190620006bf565b505060058054601260ff199182161790915560609490941b6001600160601b0319166080525050600a8054909216909155506000915062000123905033620004f7565b604051639ae4100b60e01b81526001600160a01b0380831660048301908152818b16602484015290891660448301529192507351a189ccd2eb5e1168ddca7e59f7c8f39aa5223291639ae4100b9184918b918b9189916064018260a080838360005b838110156200019f57818101518382015260200162000185565b5050505090500194505050505060006040518083038186803b158015620001c557600080fd5b505af4158015620001da573d6000803e3d6000fd5b5050506001600160601b031960608a811b82166101605289811b821660a05288811b821660c05287811b82166101005286901b166101405250426102a052825161020052602083810180516102205260408501519051620002479290919062000567811b6200236317901c565b600d556200026e620151808460026020020151620005ba60201b620023a51790919060201c565b61024052606083015161026052608083015161028052604080516374d7c62b60e01b815290516001600160a01b038316916374d7c62b916004828101926020929190829003018186803b158015620002c557600080fd5b505afa158015620002da573d6000803e3d6000fd5b505050506040513d6020811015620002f157600080fd5b50516102c05260408051631c1757c960e21b815290516001600160a01b0383169163705d5f24916004808301926020929190829003018186803b1580156200033857600080fd5b505afa1580156200034d573d6000803e3d6000fd5b505050506040513d60208110156200036457600080fd5b50516102e05281516001600160601b0319606091821b811661018052602080850151831b82166101a052604080860151841b9092166101c0523390921b6101e0528051630cf5bc1d60e11b81526001600160a01b0389811660048301529151918716926319eb783a926024808401938290030181600087803b158015620003ea57600080fd5b505af1158015620003ff573d6000803e3d6000fd5b505050506040513d60208110156200041657600080fd5b505160601b6001600160601b0319166101205260408051630cf5bc1d60e11b81526001600160a01b0389811660048301529151918716916319eb783a916024808201926020929091908290030181600087803b1580156200047657600080fd5b505af11580156200048b573d6000803e3d6000fd5b505050506040513d6020811015620004a257600080fd5b505160601b6001600160601b03191660e052604080516000815290517f400243eaf4da5ecbc2c6f2453605068a362c65ff9212fc60b58289b7e09d2209916020908290030190a1505050505050505062000764565b6000816001600160a01b031663c31245256040518163ffffffff1660e01b815260040160206040518083038186803b1580156200053357600080fd5b505afa15801562000548573d6000803e3d6000fd5b505050506040513d60208110156200055f57600080fd5b505192915050565b6000620005b183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506200061860201b60201c565b90505b92915050565b600082620005cb57506000620005b4565b82820282848281620005d957fe5b0414620005b15760405162461bcd60e51b815260040180806020018281038252602181526020018062004a6c6021913960400191505060405180910390fd5b60008183620006a85760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156200066c57818101518382015260200162000652565b50505050905090810190601f1680156200069a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581620006b557fe5b0495945050505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200070257805160ff191683800117855562000732565b8280016001018555821562000732579182015b828111156200073257825182559160200191906001019062000715565b506200074092915062000744565b5090565b6200076191905b808211156200074057600081556001016200074b565b90565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c6101405160601c6101605160601c6101805160601c6101a05160601c6101c05160601c6101e05160601c61020051610220516102405161026051610280516102a0516102c0516102e0516140dd6200098f60003980610aa7528061134f5250806113b252806115dd52806136ac5250806115b75280612008528061368b525080611eb952806120db5250806118c1528061231d52508061198852806123415280612dc55250806110a0525080611295525080610a545280610ac85280610c3152806116f252806118785280611f07528061210152806124f352806135135250806113d6528061146b52508061113852806112c2528061144352508061095252806111145280611413525080610d75528061151c52806119c95280611bed52806129b65280612e5e52806130b25250806121d3525080610a305280610c5952806119ea52806126505280612e3752806133ce525080610fcf52508061138e528061159052806118a05280612292528061314852806132e1525080610be15280611a1c5280611e895280611f52528061208b5280612626528061339f525080610c095280610d525280610e45528061156852806116ce5280611cac5280611cef5280611f7a52806120b3528061226f5280612f24528061311952806132b752806133425280613729525080610f27528061132552806129185280612b0752506140dd6000f3fe608060405234801561001057600080fd5b50600436106103da5760003560e01c806364195ba81161020a578063a9691f3f11610125578063cf09e0d0116100b8578063e48671c411610087578063e48671c4146108e5578063e74f6166146108ed578063e920b1e1146108f5578063f52ec46c14610921578063f555278814610929576103da565b8063cf09e0d01461088a578063d8d7970014610892578063da9bf6e01461089a578063dd62ed3e146108b7576103da565b8063b4eae1cb116100f4578063b4eae1cb1461086a578063c296dcba14610872578063c9f4e4901461087a578063cee9666914610882576103da565b8063a9691f3f1461084a578063aabaecd614610852578063ac7c57801461085a578063b419857014610862576103da565b8063807763ab1161019d57806395d89b411161016c57806395d89b41146107cd578063a079a4dd146107d5578063a457c2d7146107f2578063a9059cbb1461081e576103da565b8063807763ab146107695780638456cb59146107715780638905fd4f1461077957806392769d941461079f576103da565b806374d7c62b116101d957806374d7c62b1461072b578063757116a01461073357806377903e3b1461073b5780637df1f1b914610761576103da565b806364195ba8146106ed578063705d5f24146106f557806370a08231146106fd578063743e5d1d14610723576103da565b806331a7958f116102fa5780634b27ef6c1161028d5780635c975abb1161025c5780635c975abb146106cd5780635e8bdbeb146106d557806360bd1f87146106dd57806363f04b15146106e5576103da565b80634b27ef6c146106715780634be7cb14146106795780634e97415f1461069f57806357ded9c9146106c5576103da565b8063443bb293116102c9578063443bb29314610606578063469cbfdb1461062c57806346c162de146106345780634ae01cdc1461063c576103da565b806331a7958f146105c257806339509351146105ca57806339c02899146105f65780633f4ba83a146105fe576103da565b806318160ddd1161037257806324600fc31161034157806324600fc31461056857806325af34cd146105705780632c3c12161461059c578063313ce567146105a4576103da565b806318160ddd1461051a5780631935011414610522578063209b2bca1461052a57806323b872dd14610532576103da565b8063095ea7b3116103ae578063095ea7b3146104c057806309f64d08146105005780630d49b38c14610508578063175f832914610510576103da565b806241c52c146103df578063067754581461041757806306fdde031461043b5780630895326f146104b8575b600080fd5b610405600480360360208110156103f557600080fd5b50356001600160a01b0316610931565b60408051918252519081900360200190f35b61041f610950565b604080516001600160a01b039092168252519081900360200190f35b610443610974565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561047d578181015183820152602001610465565b50505050905090810190601f1680156104aa5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610405610a0a565b6104ec600480360360408110156104d657600080fd5b506001600160a01b038135169060200135610a10565b604080519115158252519081900360200190f35b61041f610a2e565b61041f610a52565b610518610a76565b005b610405610e37565b610405610e3d565b61041f610e43565b6104ec6004803603606081101561054857600080fd5b506001600160a01b03813581169160208101359091169060400135610e67565b610518610ef5565b610578610fbf565b6040518082600481111561058857fe5b60ff16815260200191505060405180910390f35b61041f610fcd565b6105ac610ff1565b6040805160ff9092168252519081900360200190f35b610405610ffa565b6104ec600480360360408110156105e057600080fd5b506001600160a01b038135169060200135611000565b610405611054565b61051861105a565b6104056004803603602081101561061c57600080fd5b50356001600160a01b031661106c565b61040561109e565b6105186110c2565b6106446110f0565b60408051958652602086019490945284840192909252606084015215156080830152519081900360a00190f35b61040561120f565b6104ec6004803603602081101561068f57600080fd5b50356001600160a01b0316611215565b610405600480360360208110156106b557600080fd5b50356001600160a01b031661122a565b610405611293565b6104ec6112b7565b61041f6112c0565b6105186112e4565b61041f611323565b610405611347565b61040561134d565b6104056004803603602081101561071357600080fd5b50356001600160a01b0316611371565b61041f61138c565b6104056113b0565b61041f6113d4565b6107436113f8565b60408051938452602084019290925282820152519081900360600190f35b61041f61151a565b61051861153e565b6105186116a1565b6105186004803603602081101561078f57600080fd5b50356001600160a01b03166116b1565b610518600480360360408110156107b557600080fd5b506001600160a01b0381351690602001351515611786565b6104436117f6565b610518600480360360208110156107eb57600080fd5b5035611857565b6104ec6004803603604081101561080857600080fd5b506001600160a01b038135169060200135611dff565b6104ec6004803603604081101561083457600080fd5b506001600160a01b038135169060200135611e6d565b610405611e81565b61041f611e87565b610405611eab565b610405611eb1565b610405611eb7565b610405611edb565b610405611ffa565b610405612000565b610405612006565b61051861202a565b610405600480360360208110156108b057600080fd5b5035612073565b610405600480360360408110156108cd57600080fd5b506001600160a01b03813581169160200135166121a0565b6104056121cb565b61041f6121d1565b6105186004803603604081101561090b57600080fd5b506001600160a01b0381351690602001356121f5565b61040561231b565b61040561233f565b6001600160a01b0381166000908152600860205260409020545b919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60038054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610a005780601f106109d557610100808354040283529160200191610a00565b820191906000526020600020905b8154815290600101906020018083116109e357829003601f168201915b5050505050905090565b600d5481565b6000610a24610a1d6123fe565b8484612402565b5060015b92915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b610a7e6124ee565b610a8860016125be565b7351a189ccd2eb5e1168ddca7e59f7c8f39aa52232639b3134e1600c547f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610af033611371565b610af8610e37565b6040518663ffffffff1660e01b815260040180868152602001858152602001846001600160a01b03166001600160a01b031681526020018381526020018281526020019550505050505060206040518083038186803b158015610b5a57600080fd5b505af4158015610b6e573d6000803e3d6000fd5b505050506040513d6020811015610b8457600080fd5b5051610bc9576040805162461bcd60e51b815260206004820152600f60248201526e4c3a4641494c45445f544f5f4c495160881b604482015290519081900360640190fd5b60408051635432274f60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000811660248301527f0000000000000000000000000000000000000000000000000000000000000000811660448301527f000000000000000000000000000000000000000000000000000000000000000016606482015281517351a189ccd2eb5e1168ddca7e59f7c8f39aa5223292635432274f9260848082019391829003018186803b158015610cb757600080fd5b505af4158015610ccb573d6000803e3d6000fd5b505050506040513d6040811015610ce157600080fd5b508051602090910151601455601355610cf8612624565b600e5460145411610d2557601454600e54610d189163ffffffff6126a216565b600e819055601555610da0565b600e54601454610d3a9163ffffffff6126a216565b60168190556000600e55610da0906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016907f00000000000000000000000000000000000000000000000000000000000000009063ffffffff6126e416565b610da86110c2565b600a805461ff001916610400179055601354601454601654601554604080519485526020850193909352838301919091526060830152517f4152c73dd2614c4f9fc35e8c9cf16013cd588c75b49a4c1673ecffdcbcda94039181900360800190a1604051600080516020613eb18339815191529060049080825b60ff16815260200191505060405180910390a1565b60025490565b600e5481565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000610e74848484612736565b610eea84610e806123fe565b610ee585604051806060016040528060288152602001613fc4602891396001600160a01b038a16600090815260016020526040812090610ebe6123fe565b6001600160a01b03168152602081019190915260400160002054919063ffffffff61286216565b612402565b5060015b9392505050565b610efd6124ee565b610f056128f9565b604080516370a0823160e01b8152306004820181905291516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169291600080516020613ef48339815191529184916370a08231916024808301926020929190829003018186803b158015610f8057600080fd5b505afa158015610f94573d6000803e3d6000fd5b505050506040513d6020811015610faa57600080fd5b505160408051918252519081900360200190a3565b600a54610100900460ff1681565b7f000000000000000000000000000000000000000000000000000000000000000081565b60055460ff1690565b60125481565b6000610a2461100d6123fe565b84610ee5856001600061101e6123fe565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff61295116565b60145481565b6110626129ab565b61106a612a42565b565b6001600160a01b038116600090815260086020526040812054610a28906110928461122a565b9063ffffffff6126a216565b7f000000000000000000000000000000000000000000000000000000000000000081565b60006110cc612ae0565b9050600081136110dc575061106a565b6110ed6110e882612b95565b612bda565b50565b60008060008060007351a189ccd2eb5e1168ddca7e59f7c8f39aa5223263f7dd03107f0000000000000000000000000000000000000000000000000000000000000000600c547f00000000000000000000000000000000000000000000000000000000000000006040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001838152602001826001600160a01b03166001600160a01b03168152602001935050505060a06040518083038186803b1580156111bd57600080fd5b505af41580156111d1573d6000803e3d6000fd5b505050506040513d60a08110156111e757600080fd5b5080516020820151604083015160608401516080909401519299919850965091945092509050565b60155481565b600b6020526000908152604090205460ff1681565b6001600160a01b038116600090815260076020526040812054600160801b90611285906112809061127461126f61126088611371565b6006549063ffffffff6123a516565b612cda565b9063ffffffff612d1b16565b612b95565b8161128c57fe5b0492915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600a5460ff1690565b7f000000000000000000000000000000000000000000000000000000000000000081565b6112ec6124ee565b6112f660016125be565b60008060006113036113f8565b9250925092506000600d8190555061131e8383836000612d80565b505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600f5481565b7f000000000000000000000000000000000000000000000000000000000000000081565b6001600160a01b031660009081526020819052604090205490565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b600c546040805163348f8f0560e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260248201939093527f0000000000000000000000000000000000000000000000000000000000000000831660448201527f000000000000000000000000000000000000000000000000000000000000000092909216606483015251600091829182917351a189ccd2eb5e1168ddca7e59f7c8f39aa522329163691f1e0a91608480820192606092909190829003018186803b1580156114d757600080fd5b505af41580156114eb573d6000803e3d6000fd5b505050506040513d606081101561150157600080fd5b5080516020820151604090920151909591945092509050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6115466124ee565b61155060006125be565b604080516306742b0f60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f00000000000000000000000000000000000000000000000000000000000000001660248201527f000000000000000000000000000000000000000000000000000000000000000060448201527f0000000000000000000000000000000000000000000000000000000000000000606482015290517351a189ccd2eb5e1168ddca7e59f7c8f39aa52232916306742b0f916084808301926020929190829003018186803b15801561163e57600080fd5b505af4158015611652573d6000803e3d6000fd5b505050506040513d602081101561166857600080fd5b50516012556116756110c2565b600a805461ff001916610300179055604051600080516020613eb1833981519152906003908082610e22565b6116a96129ab565b61106a612fd7565b7351a189ccd2eb5e1168ddca7e59f7c8f39aa5223263a89d5ddb827f00000000000000000000000000000000000000000000000000000000000000006117167f0000000000000000000000000000000000000000000000000000000000000000613058565b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015292841660248401529216604482015290516064808301926000929190829003018186803b15801561176b57600080fd5b505af415801561177f573d6000803e3d6000fd5b5050505050565b61178e6124ee565b6117966130a7565b6001600160a01b0382166000818152600b6020908152604091829020805460ff1916851515908117909155825190815291517fa30926bb66c297ef5b745add0851be86e54885064eeb08b3dec89c878e53e9e69281900390910190a25050565b60048054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610a005780601f106109d557610100808354040283529160200191610a00565b61185f6124ee565b6118676130a7565b61187160006125be565b600061189c7f0000000000000000000000000000000000000000000000000000000000000000613058565b90507f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083101561192b576040805162461bcd60e51b8152602060048201526014602482015273130e90535517d31517d49154555154d517d0535560621b604482015290519081900360640190fd5b611933613115565b83111561197d576040805162461bcd60e51b8152602060048201526013602482015272130e90535517d1d517d1955391115117d05355606a1b604482015290519081900360640190fd5b600e8390556119b2427f000000000000000000000000000000000000000000000000000000000000000063ffffffff61295116565b600c55600a805461ff001916610100179055611a4a7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611a1286612073565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001692919063ffffffff6131de16565b6000826001600160a01b031663cc32d1766040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8557600080fd5b505afa158015611a99573d6000803e3d6000fd5b505050506040513d6020811015611aaf57600080fd5b505160408051630b5096bd60e11b815290519192506000916001600160a01b038616916316a12d7a916004808301926020929190829003018186803b158015611af757600080fd5b505afa158015611b0b573d6000803e3d6000fd5b505050506040513d6020811015611b2157600080fd5b50516040805163a5a2760560e01b815290519192506000916001600160a01b0387169163a5a27605916004808301926020929190829003018186803b158015611b6957600080fd5b505afa158015611b7d573d6000803e3d6000fd5b505050506040513d6020811015611b9357600080fd5b505190506000611bbb612710611baf898663ffffffff6123a516565b9063ffffffff61236316565b601181905590506000611bda612710611baf8a8863ffffffff6123a516565b9050611be7868483613238565b611c25867f0000000000000000000000000000000000000000000000000000000000000000611c20856110928d8763ffffffff6126a216565b613238565b611c3182611092613115565b601281905550856001600160a01b0316639890220b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611c7257600080fd5b505af1158015611c86573d6000803e3d6000fd5b50505050611c926110c2565b611c9a612624565b611ca26132b5565b611caa613320565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316600080516020613ef48339815191527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231876040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015611d6357600080fd5b505afa158015611d77573d6000803e3d6000fd5b505050506040513d6020811015611d8d57600080fd5b505160408051918252519081900360200190a360408051600181529051600080516020613eb18339815191529181900360200190a16040805189815290517febf485edb8aa02238294ff7cda84b77f5afafa105e34f3bbf866534b7b5bd40e9181900360200190a15050505050505050565b6000610a24611e0c6123fe565b84610ee5856040518060600160405280602581526020016140836025913960016000611e366123fe565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff61286216565b6000610a24611e7a6123fe565b8484612736565b60095481565b7f000000000000000000000000000000000000000000000000000000000000000081565b60115481565b600c5481565b7f000000000000000000000000000000000000000000000000000000000000000081565b600080611ee661339b565b90507395f9676a34af2675b63948ddba8f8c798741a52a63c1e37186611f2b7f0000000000000000000000000000000000000000000000000000000000000000613058565b6040805160e084901b6001600160e01b03191681526001600160a01b0392831660048201527f0000000000000000000000000000000000000000000000000000000000000000831660248201527f0000000000000000000000000000000000000000000000000000000000000000909216604483015260648201859052516084808301926020929190829003018186803b158015611fc857600080fd5b505af4158015611fdc573d6000803e3d6000fd5b505050506040513d6020811015611ff257600080fd5b505191505090565b60135481565b60165481565b7f000000000000000000000000000000000000000000000000000000000000000081565b6120326124ee565b61203c60016125be565b60008060008061204a6110f0565b600d8054600019019055939750919550935090915061206d905084848484612d80565b50505050565b60408051630f6a160160e31b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000811660248301527f000000000000000000000000000000000000000000000000000000000000000060448301527f00000000000000000000000000000000000000000000000000000000000000001660648201526084810183905290516000917351a189ccd2eb5e1168ddca7e59f7c8f39aa5223291637b50b0089160a480820192602092909190829003018186803b15801561216e57600080fd5b505af4158015612182573d6000803e3d6000fd5b505050506040513d602081101561219857600080fd5b505192915050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60105481565b7f000000000000000000000000000000000000000000000000000000000000000081565b600a5460ff1615612240576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6122486124ee565b61225260006125be565b61225a613433565b612262613686565b6122bd6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016337f00000000000000000000000000000000000000000000000000000000000000008463ffffffff6131de16565b60006122c882613722565b90506122d483826137c7565b6040805183815290516001600160a01b038516917f726d5f1a838fe31748f737fa3ae5539ccff95952adfc593a1299532b643ff7a8919081900360200190a261131e6132b5565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000610eee83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061386e565b6000826123b457506000610a28565b828202828482816123c157fe5b0414610eee5760405162461bcd60e51b8152600401808060200182810382526021815260200180613fa36021913960400191505060405180910390fd5b3390565b6001600160a01b0383166124475760405162461bcd60e51b81526004018080602001828103825260248152602001806140116024913960400191505060405180910390fd5b6001600160a01b03821661248c5760405162461bcd60e51b8152600401808060200182810382526022815260200180613f146022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6125177f0000000000000000000000000000000000000000000000000000000000000000613058565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561254f57600080fd5b505afa158015612563573d6000803e3d6000fd5b505050506040513d602081101561257957600080fd5b50511561106a576040805162461bcd60e51b815260206004820152600e60248201526d130e941493d513d7d4105554d15160921b604482015290519081900360640190fd5b8060048111156125ca57fe5b600a54610100900460ff1660048111156125e057fe5b146110ed576040805162461bcd60e51b815260206004820152600f60248201526e4c3a494e56414c49445f535441544560881b604482015290519081900360640190fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600080516020613ef483398151915261268f61339b565b60408051918252519081900360200190a3565b6000610eee83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612862565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261131e9084906138d3565b612741838383613984565b600061275b61126f836006546123a590919063ffffffff16565b6001600160a01b03851660009081526007602052604081205491925090612788908363ffffffff612d1b16565b6001600160a01b03808716600090815260076020526040808220849055918716815290812054919250906127c2908463ffffffff613aeb16565b6001600160a01b0380871660009081526007602090815260409182902084905581518681529151939450918916927ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773929181900390910190a26040805182815290516001600160a01b038716917ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773919081900360200190a2505050505050565b600081848411156128f15760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156128b657818101518382015260200161289e565b50505050905090810190601f1680156128e35780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000612903613b50565b905080156110ed576129456001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016338363ffffffff6126e416565b61294d612ae0565b5050565b600082820183811015610eee576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806129f15750336000908152600b602052604090205460ff165b61106a576040805162461bcd60e51b815260206004820152601760248201527f4c3a4e4f545f424f52524f5745525f4f525f41444d494e000000000000000000604482015290519081900360640190fd5b600a5460ff16612a90576040805162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015290519081900360640190fd5b600a805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa612ac36123fe565b604080516001600160a01b039092168252519081900360200190a1565b600954604080516370a0823160e01b81523060048201529051600092916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916370a0823191602480820192602092909190829003018186803b158015612b4e57600080fd5b505afa158015612b62573d6000803e3d6000fd5b505050506040513d6020811015612b7857600080fd5b50516009819055612b8f908263ffffffff613aeb16565b91505090565b600080821215612bd6576040805162461bcd60e51b8152602060048201526007602482015266534d493a4e454760c81b604482015290519081900360640190fd5b5090565b6000612be4610e37565b11612c28576040805162461bcd60e51b815260206004820152600f60248201526e4644543a5a45524f5f535550504c5960881b604482015290519081900360640190fd5b80612c32576110ed565b612c69612c3d610e37565b612c5183600160801b63ffffffff6123a516565b81612c5857fe5b60065491900463ffffffff61295116565b60065560408051828152905133917f26536799ace2c3dbe12e638ec3ade6b4173dcf1289be0a58d51a5003015649bd919081900360200190a260065460408051918252517f1f8d7705f31c3337a080803a8ad7e71946fb88d84738879be2bf402f97156e969181900360200190a150565b80600081121561094b576040805162461bcd60e51b815260206004820152600760248201526629a6aa9d27a7a160c91b604482015290519081900360640190fd5b6000828201818312801590612d305750838112155b80612d455750600083128015612d4557508381125b610eee5760405162461bcd60e51b8152600401808060200182810382526021815260200180613f5c6021913960400191505060405180910390fd5b600d54601054612d96908463ffffffff61295116565b6010558315612db657600f54612db2908563ffffffff61295116565b600f555b8015612e1457600c54612def907f000000000000000000000000000000000000000000000000000000000000000063ffffffff61295116565b600c558315612e0f57600e54612e0b908563ffffffff6126a216565b600e555b612f17565b6000600e819055600a805461ff001916610200179055600c556001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663f2d5d56b7f0000000000000000000000000000000000000000000000000000000000000000612e8561339b565b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015612ed457600080fd5b505af1158015612ee8573d6000803e3d6000fd5b50505050612ef4612624565b60408051600281529051600080516020613eb18339815191529181900360200190a15b612f526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633308863ffffffff6131de16565b612f5a6110c2565b7fd0eb4a53827b5b6d9df4e56deb84ebbed98927c1d73f2468eef32f3c286d7a6085858584600e5460008711612f91576000612f95565b600c545b604080519687526020870195909552858501939093526060850191909152608084015260a083015284151560c0830152519081900360e00190a161177f613320565b600a5460ff1615613022576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b600a805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612ac36123fe565b6000816001600160a01b031663c31245256040518163ffffffff1660e01b815260040160206040518083038186803b15801561309357600080fd5b505afa158015612182573d6000803e3d6000fd5b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461106a576040805162461bcd60e51b815260206004820152600e60248201526d261d2727aa2fa127a92927aba2a960911b604482015290519081900360640190fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a082317f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156131ad57600080fd5b505afa1580156131c1573d6000803e3d6000fd5b505050506040513d60208110156131d757600080fd5b5051905090565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b17905261206d9085906138d3565b826001600160a01b031663f2d5d56b83836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561329857600080fd5b505af11580156132ac573d6000803e3d6000fd5b50505050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600080516020613ef483398151915261268f613115565b604080516370a0823160e01b8152306004820181905291516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169291600080516020613ef48339815191529184916370a08231916024808301926020929190829003018186803b158015610f8057600080fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a082317f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156131ad57600080fd5b6000336001600160a01b03166316f0115b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561346e57600080fd5b505afa158015613482573d6000803e3d6000fd5b505050506040513d602081101561349857600080fd5b5051604080516303526ce360e21b815290519192506000916001600160a01b03841691630d49b38c916004808301926020929190829003018186803b1580156134e057600080fd5b505afa1580156134f4573d6000803e3d6000fd5b505050506040513d602081101561350a57600080fd5b505190506135377f0000000000000000000000000000000000000000000000000000000000000000613058565b6001600160a01b031663107c0240826040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561358c57600080fd5b505afa1580156135a0573d6000803e3d6000fd5b505050506040513d60208110156135b657600080fd5b505180156136425750806001600160a01b0316635b16ebb7836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561361557600080fd5b505afa158015613629573d6000803e3d6000fd5b505050506040513d602081101561363f57600080fd5b50515b61294d576040805162461bcd60e51b815260206004820152601060248201526f261d24a72b20a624a22fa622a72222a960811b604482015290519081900360640190fd5b6136d67f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000063ffffffff61295116565b42111561106a576040805162461bcd60e51b8152602060048201526015602482015274130e941054d517d1955391125391d7d411549253d1605a1b604482015290519081900360640190fd5b6000610a287f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561378057600080fd5b505afa158015613794573d6000803e3d6000fd5b505050506040513d60208110156137aa57600080fd5b5051600a0a611baf84670de0b6b3a764000063ffffffff6123a516565b6137d18282613bd5565b60006138136137ee61126f846006546123a590919063ffffffff16565b6001600160a01b0385166000908152600760205260409020549063ffffffff613aeb16565b6001600160a01b0384166000818152600760209081526040918290208490558151848152915193945091927ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773929181900390910190a2505050565b600081836138bd5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156128b657818101518382015260200161289e565b5060008385816138c957fe5b0495945050505050565b6060613928826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613cd19092919063ffffffff16565b80519091501561131e5780806020019051602081101561394757600080fd5b505161131e5760405162461bcd60e51b815260040180806020018281038252602a815260200180614059602a913960400191505060405180910390fd5b6001600160a01b0383166139c95760405162461bcd60e51b8152600401808060200182810382526025815260200180613fec6025913960400191505060405180910390fd5b6001600160a01b038216613a0e5760405162461bcd60e51b8152600401808060200182810382526023815260200180613ed16023913960400191505060405180910390fd5b613a1983838361131e565b613a5c81604051806060016040528060268152602001613f36602691396001600160a01b038616600090815260208190526040902054919063ffffffff61286216565b6001600160a01b038085166000908152602081905260408082209390935590841681522054613a91908263ffffffff61295116565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b6000818303818312801590613b005750838113155b80613b155750600083128015613b1557508381135b610eee5760405162461bcd60e51b81526004018080602001828103825260248152602001806140356024913960400191505060405180910390fd5b6000613b5b3361106c565b3360009081526008602052604081205491925090613b7f908363ffffffff61295116565b336000818152600860209081526040918290208490558151868152908101849052815193945091927ffbc3a599b784fe88772fc5abcc07223f64ca0b13acc341f4fb1e46bef0510eb49281900390910190a25090565b6001600160a01b038216613c30576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b613c3c6000838361131e565b600254613c4f908263ffffffff61295116565b6002556001600160a01b038216600090815260208190526040902054613c7b908263ffffffff61295116565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6060613ce08484600085613ce8565b949350505050565b606082471015613d295760405162461bcd60e51b8152600401808060200182810382526026815260200180613f7d6026913960400191505060405180910390fd5b613d3285613e44565b613d83576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310613dc25780518252601f199092019160209182019101613da3565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613e24576040519150601f19603f3d011682016040523d82523d6000602084013e613e29565b606091505b5091509150613e39828286613e4a565b979650505050505050565b3b151590565b60608315613e59575081610eee565b825115613e695782518084602001fd5b60405162461bcd60e51b81526020600482018181528451602484015284518593919283926044019190850190808383600083156128b657818101518382015260200161289e56fe400243eaf4da5ecbc2c6f2453605068a362c65ff9212fc60b58289b7e09d220945524332303a207472616e7366657220746f20746865207a65726f20616464726573732047d1633ff7768462ae07d28cb16e484203bfd6d85ce832494270ebcd9081a245524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63655369676e6564536166654d6174683a206164646974696f6e206f766572666c6f77416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f20616464726573735369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656445524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220d2590c75d517c40c26e5ced3921915b365df606e193d51a42f29b7cd42d4b47f64736f6c634300060b0033536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7700000000000000000000000061be14477ca25571e197ecc6addb895c67618010000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5990000000000000000000000000eb96a53ec793a244876b018073f33b23000f25b000000000000000000000000ee3e59d381968f4f9c92460d9d5cfcf5d3a67987000000000000000000000000000000000000000000000000000000000000044c000000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000001d1a94a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007d622bb6ed13a599ec96366fa95f2452c64ce6020000000000000000000000008dc5aa328142aa8a008c25f66a77eaa8e4b46f3c000000000000000000000000e88ab4cf1ec06840d16fed69c964ad9daff5c6c2
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103da5760003560e01c806364195ba81161020a578063a9691f3f11610125578063cf09e0d0116100b8578063e48671c411610087578063e48671c4146108e5578063e74f6166146108ed578063e920b1e1146108f5578063f52ec46c14610921578063f555278814610929576103da565b8063cf09e0d01461088a578063d8d7970014610892578063da9bf6e01461089a578063dd62ed3e146108b7576103da565b8063b4eae1cb116100f4578063b4eae1cb1461086a578063c296dcba14610872578063c9f4e4901461087a578063cee9666914610882576103da565b8063a9691f3f1461084a578063aabaecd614610852578063ac7c57801461085a578063b419857014610862576103da565b8063807763ab1161019d57806395d89b411161016c57806395d89b41146107cd578063a079a4dd146107d5578063a457c2d7146107f2578063a9059cbb1461081e576103da565b8063807763ab146107695780638456cb59146107715780638905fd4f1461077957806392769d941461079f576103da565b806374d7c62b116101d957806374d7c62b1461072b578063757116a01461073357806377903e3b1461073b5780637df1f1b914610761576103da565b806364195ba8146106ed578063705d5f24146106f557806370a08231146106fd578063743e5d1d14610723576103da565b806331a7958f116102fa5780634b27ef6c1161028d5780635c975abb1161025c5780635c975abb146106cd5780635e8bdbeb146106d557806360bd1f87146106dd57806363f04b15146106e5576103da565b80634b27ef6c146106715780634be7cb14146106795780634e97415f1461069f57806357ded9c9146106c5576103da565b8063443bb293116102c9578063443bb29314610606578063469cbfdb1461062c57806346c162de146106345780634ae01cdc1461063c576103da565b806331a7958f146105c257806339509351146105ca57806339c02899146105f65780633f4ba83a146105fe576103da565b806318160ddd1161037257806324600fc31161034157806324600fc31461056857806325af34cd146105705780632c3c12161461059c578063313ce567146105a4576103da565b806318160ddd1461051a5780631935011414610522578063209b2bca1461052a57806323b872dd14610532576103da565b8063095ea7b3116103ae578063095ea7b3146104c057806309f64d08146105005780630d49b38c14610508578063175f832914610510576103da565b806241c52c146103df578063067754581461041757806306fdde031461043b5780630895326f146104b8575b600080fd5b610405600480360360208110156103f557600080fd5b50356001600160a01b0316610931565b60408051918252519081900360200190f35b61041f610950565b604080516001600160a01b039092168252519081900360200190f35b610443610974565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561047d578181015183820152602001610465565b50505050905090810190601f1680156104aa5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610405610a0a565b6104ec600480360360408110156104d657600080fd5b506001600160a01b038135169060200135610a10565b604080519115158252519081900360200190f35b61041f610a2e565b61041f610a52565b610518610a76565b005b610405610e37565b610405610e3d565b61041f610e43565b6104ec6004803603606081101561054857600080fd5b506001600160a01b03813581169160208101359091169060400135610e67565b610518610ef5565b610578610fbf565b6040518082600481111561058857fe5b60ff16815260200191505060405180910390f35b61041f610fcd565b6105ac610ff1565b6040805160ff9092168252519081900360200190f35b610405610ffa565b6104ec600480360360408110156105e057600080fd5b506001600160a01b038135169060200135611000565b610405611054565b61051861105a565b6104056004803603602081101561061c57600080fd5b50356001600160a01b031661106c565b61040561109e565b6105186110c2565b6106446110f0565b60408051958652602086019490945284840192909252606084015215156080830152519081900360a00190f35b61040561120f565b6104ec6004803603602081101561068f57600080fd5b50356001600160a01b0316611215565b610405600480360360208110156106b557600080fd5b50356001600160a01b031661122a565b610405611293565b6104ec6112b7565b61041f6112c0565b6105186112e4565b61041f611323565b610405611347565b61040561134d565b6104056004803603602081101561071357600080fd5b50356001600160a01b0316611371565b61041f61138c565b6104056113b0565b61041f6113d4565b6107436113f8565b60408051938452602084019290925282820152519081900360600190f35b61041f61151a565b61051861153e565b6105186116a1565b6105186004803603602081101561078f57600080fd5b50356001600160a01b03166116b1565b610518600480360360408110156107b557600080fd5b506001600160a01b0381351690602001351515611786565b6104436117f6565b610518600480360360208110156107eb57600080fd5b5035611857565b6104ec6004803603604081101561080857600080fd5b506001600160a01b038135169060200135611dff565b6104ec6004803603604081101561083457600080fd5b506001600160a01b038135169060200135611e6d565b610405611e81565b61041f611e87565b610405611eab565b610405611eb1565b610405611eb7565b610405611edb565b610405611ffa565b610405612000565b610405612006565b61051861202a565b610405600480360360208110156108b057600080fd5b5035612073565b610405600480360360408110156108cd57600080fd5b506001600160a01b03813581169160200135166121a0565b6104056121cb565b61041f6121d1565b6105186004803603604081101561090b57600080fd5b506001600160a01b0381351690602001356121f5565b61040561231b565b61040561233f565b6001600160a01b0381166000908152600860205260409020545b919050565b7f0000000000000000000000007d622bb6ed13a599ec96366fa95f2452c64ce60281565b60038054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610a005780601f106109d557610100808354040283529160200191610a00565b820191906000526020600020905b8154815290600101906020018083116109e357829003601f168201915b5050505050905090565b600d5481565b6000610a24610a1d6123fe565b8484612402565b5060015b92915050565b7f0000000000000000000000000cb4fb75e14358fb563cb815dc02e16fdf2a521881565b7f000000000000000000000000908cc851bc757248514e060ad8bd0a03908308ee81565b610a7e6124ee565b610a8860016125be565b7351a189ccd2eb5e1168ddca7e59f7c8f39aa52232639b3134e1600c547f00000000000000000000000000000000000000000000000000000000000697807f000000000000000000000000908cc851bc757248514e060ad8bd0a03908308ee610af033611371565b610af8610e37565b6040518663ffffffff1660e01b815260040180868152602001858152602001846001600160a01b03166001600160a01b031681526020018381526020018281526020019550505050505060206040518083038186803b158015610b5a57600080fd5b505af4158015610b6e573d6000803e3d6000fd5b505050506040513d6020811015610b8457600080fd5b5051610bc9576040805162461bcd60e51b815260206004820152600f60248201526e4c3a4641494c45445f544f5f4c495160881b604482015290519081900360640190fd5b60408051635432274f60e01b81526001600160a01b037f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599811660048301527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48811660248301527f000000000000000000000000908cc851bc757248514e060ad8bd0a03908308ee811660448301527f0000000000000000000000000cb4fb75e14358fb563cb815dc02e16fdf2a521816606482015281517351a189ccd2eb5e1168ddca7e59f7c8f39aa5223292635432274f9260848082019391829003018186803b158015610cb757600080fd5b505af4158015610ccb573d6000803e3d6000fd5b505050506040513d6040811015610ce157600080fd5b508051602090910151601455601355610cf8612624565b600e5460145411610d2557601454600e54610d189163ffffffff6126a216565b600e819055601555610da0565b600e54601454610d3a9163ffffffff6126a216565b60168190556000600e55610da0906001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816907f00000000000000000000000061be14477ca25571e197ecc6addb895c676180109063ffffffff6126e416565b610da86110c2565b600a805461ff001916610400179055601354601454601654601554604080519485526020850193909352838301919091526060830152517f4152c73dd2614c4f9fc35e8c9cf16013cd588c75b49a4c1673ecffdcbcda94039181900360800190a1604051600080516020613eb18339815191529060049080825b60ff16815260200191505060405180910390a1565b60025490565b600e5481565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b6000610e74848484612736565b610eea84610e806123fe565b610ee585604051806060016040528060288152602001613fc4602891396001600160a01b038a16600090815260016020526040812090610ebe6123fe565b6001600160a01b03168152602081019190915260400160002054919063ffffffff61286216565b612402565b5060015b9392505050565b610efd6124ee565b610f056128f9565b604080516370a0823160e01b8152306004820181905291516001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169291600080516020613ef48339815191529184916370a08231916024808301926020929190829003018186803b158015610f8057600080fd5b505afa158015610f94573d6000803e3d6000fd5b505050506040513d6020811015610faa57600080fd5b505160408051918252519081900360200190a3565b600a54610100900460ff1681565b7f0000000000000000000000000eb96a53ec793a244876b018073f33b23000f25b81565b60055460ff1690565b60125481565b6000610a2461100d6123fe565b84610ee5856001600061101e6123fe565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff61295116565b60145481565b6110626129ab565b61106a612a42565b565b6001600160a01b038116600090815260086020526040812054610a28906110928461122a565b9063ffffffff6126a216565b7f000000000000000000000000000000000000000000000000000000000000005a81565b60006110cc612ae0565b9050600081136110dc575061106a565b6110ed6110e882612b95565b612bda565b50565b60008060008060007351a189ccd2eb5e1168ddca7e59f7c8f39aa5223263f7dd03107f0000000000000000000000007d622bb6ed13a599ec96366fa95f2452c64ce602600c547f0000000000000000000000008dc5aa328142aa8a008c25f66a77eaa8e4b46f3c6040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001838152602001826001600160a01b03166001600160a01b03168152602001935050505060a06040518083038186803b1580156111bd57600080fd5b505af41580156111d1573d6000803e3d6000fd5b505050506040513d60a08110156111e757600080fd5b5080516020820151604083015160608401516080909401519299919850965091945092509050565b60155481565b600b6020526000908152604090205460ff1681565b6001600160a01b038116600090815260076020526040812054600160801b90611285906112809061127461126f61126088611371565b6006549063ffffffff6123a516565b612cda565b9063ffffffff612d1b16565b612b95565b8161128c57fe5b0492915050565b7f000000000000000000000000000000000000000000000000000000000000044c81565b600a5460ff1690565b7f0000000000000000000000008dc5aa328142aa8a008c25f66a77eaa8e4b46f3c81565b6112ec6124ee565b6112f660016125be565b60008060006113036113f8565b9250925092506000600d8190555061131e8383836000612d80565b505050565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b600f5481565b7f000000000000000000000000000000000000000000000000000000000006978081565b6001600160a01b031660009081526020819052604090205490565b7f000000000000000000000000058550063391330b10ab4c93d0762a767fdd2a4f81565b7f00000000000000000000000000000000000000000000000000000000000d2f0081565b7f000000000000000000000000e88ab4cf1ec06840d16fed69c964ad9daff5c6c281565b600c546040805163348f8f0560e11b81526001600160a01b037f0000000000000000000000007d622bb6ed13a599ec96366fa95f2452c64ce6028116600483015260248201939093527f0000000000000000000000008dc5aa328142aa8a008c25f66a77eaa8e4b46f3c831660448201527f000000000000000000000000e88ab4cf1ec06840d16fed69c964ad9daff5c6c292909216606483015251600091829182917351a189ccd2eb5e1168ddca7e59f7c8f39aa522329163691f1e0a91608480820192606092909190829003018186803b1580156114d757600080fd5b505af41580156114eb573d6000803e3d6000fd5b505050506040513d606081101561150157600080fd5b5080516020820151604090920151909591945092509050565b7f00000000000000000000000061be14477ca25571e197ecc6addb895c6761801081565b6115466124ee565b61155060006125be565b604080516306742b0f60e01b81526001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48811660048301527f000000000000000000000000058550063391330b10ab4c93d0762a767fdd2a4f1660248201527f0000000000000000000000000000000000000000000000000000000060ed16c760448201527f00000000000000000000000000000000000000000000000000000000000d2f00606482015290517351a189ccd2eb5e1168ddca7e59f7c8f39aa52232916306742b0f916084808301926020929190829003018186803b15801561163e57600080fd5b505af4158015611652573d6000803e3d6000fd5b505050506040513d602081101561166857600080fd5b50516012556116756110c2565b600a805461ff001916610300179055604051600080516020613eb1833981519152906003908082610e22565b6116a96129ab565b61106a612fd7565b7351a189ccd2eb5e1168ddca7e59f7c8f39aa5223263a89d5ddb827f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486117167f000000000000000000000000908cc851bc757248514e060ad8bd0a03908308ee613058565b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015292841660248401529216604482015290516064808301926000929190829003018186803b15801561176b57600080fd5b505af415801561177f573d6000803e3d6000fd5b5050505050565b61178e6124ee565b6117966130a7565b6001600160a01b0382166000818152600b6020908152604091829020805460ff1916851515908117909155825190815291517fa30926bb66c297ef5b745add0851be86e54885064eeb08b3dec89c878e53e9e69281900390910190a25050565b60048054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610a005780601f106109d557610100808354040283529160200191610a00565b61185f6124ee565b6118676130a7565b61187160006125be565b600061189c7f000000000000000000000000908cc851bc757248514e060ad8bd0a03908308ee613058565b90507f000000000000000000000000058550063391330b10ab4c93d0762a767fdd2a4f7f000000000000000000000000000000000000000000000000000001d1a94a200083101561192b576040805162461bcd60e51b8152602060048201526014602482015273130e90535517d31517d49154555154d517d0535560621b604482015290519081900360640190fd5b611933613115565b83111561197d576040805162461bcd60e51b8152602060048201526013602482015272130e90535517d1d517d1955391115117d05355606a1b604482015290519081900360640190fd5b600e8390556119b2427f0000000000000000000000000000000000000000000000000000000000278d0063ffffffff61295116565b600c55600a805461ff001916610100179055611a4a7f00000000000000000000000061be14477ca25571e197ecc6addb895c676180107f0000000000000000000000000cb4fb75e14358fb563cb815dc02e16fdf2a5218611a1286612073565b6001600160a01b037f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5991692919063ffffffff6131de16565b6000826001600160a01b031663cc32d1766040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8557600080fd5b505afa158015611a99573d6000803e3d6000fd5b505050506040513d6020811015611aaf57600080fd5b505160408051630b5096bd60e11b815290519192506000916001600160a01b038616916316a12d7a916004808301926020929190829003018186803b158015611af757600080fd5b505afa158015611b0b573d6000803e3d6000fd5b505050506040513d6020811015611b2157600080fd5b50516040805163a5a2760560e01b815290519192506000916001600160a01b0387169163a5a27605916004808301926020929190829003018186803b158015611b6957600080fd5b505afa158015611b7d573d6000803e3d6000fd5b505050506040513d6020811015611b9357600080fd5b505190506000611bbb612710611baf898663ffffffff6123a516565b9063ffffffff61236316565b601181905590506000611bda612710611baf8a8863ffffffff6123a516565b9050611be7868483613238565b611c25867f00000000000000000000000061be14477ca25571e197ecc6addb895c67618010611c20856110928d8763ffffffff6126a216565b613238565b611c3182611092613115565b601281905550856001600160a01b0316639890220b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611c7257600080fd5b505af1158015611c86573d6000803e3d6000fd5b50505050611c926110c2565b611c9a612624565b611ca26132b5565b611caa613320565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316836001600160a01b0316600080516020613ef48339815191527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03166370a08231876040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015611d6357600080fd5b505afa158015611d77573d6000803e3d6000fd5b505050506040513d6020811015611d8d57600080fd5b505160408051918252519081900360200190a360408051600181529051600080516020613eb18339815191529181900360200190a16040805189815290517febf485edb8aa02238294ff7cda84b77f5afafa105e34f3bbf866534b7b5bd40e9181900360200190a15050505050505050565b6000610a24611e0c6123fe565b84610ee5856040518060600160405280602581526020016140836025913960016000611e366123fe565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff61286216565b6000610a24611e7a6123fe565b8484612736565b60095481565b7f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59981565b60115481565b600c5481565b7f000000000000000000000000000000000000000000000000000000000000000081565b600080611ee661339b565b90507395f9676a34af2675b63948ddba8f8c798741a52a63c1e37186611f2b7f000000000000000000000000908cc851bc757248514e060ad8bd0a03908308ee613058565b6040805160e084901b6001600160e01b03191681526001600160a01b0392831660048201527f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599831660248201527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48909216604483015260648201859052516084808301926020929190829003018186803b158015611fc857600080fd5b505af4158015611fdc573d6000803e3d6000fd5b505050506040513d6020811015611ff257600080fd5b505191505090565b60135481565b60165481565b7f0000000000000000000000000000000000000000000000000000000060ed16c781565b6120326124ee565b61203c60016125be565b60008060008061204a6110f0565b600d8054600019019055939750919550935090915061206d905084848484612d80565b50505050565b60408051630f6a160160e31b81526001600160a01b037f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599811660048301527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48811660248301527f000000000000000000000000000000000000000000000000000000000000000060448301527f000000000000000000000000908cc851bc757248514e060ad8bd0a03908308ee1660648201526084810183905290516000917351a189ccd2eb5e1168ddca7e59f7c8f39aa5223291637b50b0089160a480820192602092909190829003018186803b15801561216e57600080fd5b505af4158015612182573d6000803e3d6000fd5b505050506040513d602081101561219857600080fd5b505192915050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60105481565b7f000000000000000000000000ee3e59d381968f4f9c92460d9d5cfcf5d3a6798781565b600a5460ff1615612240576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6122486124ee565b61225260006125be565b61225a613433565b612262613686565b6122bd6001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816337f000000000000000000000000058550063391330b10ab4c93d0762a767fdd2a4f8463ffffffff6131de16565b60006122c882613722565b90506122d483826137c7565b6040805183815290516001600160a01b038516917f726d5f1a838fe31748f737fa3ae5539ccff95952adfc593a1299532b643ff7a8919081900360200190a261131e6132b5565b7f000000000000000000000000000000000000000000000000000001d1a94a200081565b7f0000000000000000000000000000000000000000000000000000000000278d0081565b6000610eee83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061386e565b6000826123b457506000610a28565b828202828482816123c157fe5b0414610eee5760405162461bcd60e51b8152600401808060200182810382526021815260200180613fa36021913960400191505060405180910390fd5b3390565b6001600160a01b0383166124475760405162461bcd60e51b81526004018080602001828103825260248152602001806140116024913960400191505060405180910390fd5b6001600160a01b03821661248c5760405162461bcd60e51b8152600401808060200182810382526022815260200180613f146022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6125177f000000000000000000000000908cc851bc757248514e060ad8bd0a03908308ee613058565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801561254f57600080fd5b505afa158015612563573d6000803e3d6000fd5b505050506040513d602081101561257957600080fd5b50511561106a576040805162461bcd60e51b815260206004820152600e60248201526d130e941493d513d7d4105554d15160921b604482015290519081900360640190fd5b8060048111156125ca57fe5b600a54610100900460ff1660048111156125e057fe5b146110ed576040805162461bcd60e51b815260206004820152600f60248201526e4c3a494e56414c49445f535441544560881b604482015290519081900360640190fd5b7f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5996001600160a01b03167f0000000000000000000000000cb4fb75e14358fb563cb815dc02e16fdf2a52186001600160a01b0316600080516020613ef483398151915261268f61339b565b60408051918252519081900360200190a3565b6000610eee83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612862565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261131e9084906138d3565b612741838383613984565b600061275b61126f836006546123a590919063ffffffff16565b6001600160a01b03851660009081526007602052604081205491925090612788908363ffffffff612d1b16565b6001600160a01b03808716600090815260076020526040808220849055918716815290812054919250906127c2908463ffffffff613aeb16565b6001600160a01b0380871660009081526007602090815260409182902084905581518681529151939450918916927ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773929181900390910190a26040805182815290516001600160a01b038716917ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773919081900360200190a2505050505050565b600081848411156128f15760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156128b657818101518382015260200161289e565b50505050905090810190601f1680156128e35780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000612903613b50565b905080156110ed576129456001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816338363ffffffff6126e416565b61294d612ae0565b5050565b600082820183811015610eee576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b336001600160a01b037f00000000000000000000000061be14477ca25571e197ecc6addb895c676180101614806129f15750336000908152600b602052604090205460ff165b61106a576040805162461bcd60e51b815260206004820152601760248201527f4c3a4e4f545f424f52524f5745525f4f525f41444d494e000000000000000000604482015290519081900360640190fd5b600a5460ff16612a90576040805162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015290519081900360640190fd5b600a805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa612ac36123fe565b604080516001600160a01b039092168252519081900360200190a1565b600954604080516370a0823160e01b81523060048201529051600092916001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816916370a0823191602480820192602092909190829003018186803b158015612b4e57600080fd5b505afa158015612b62573d6000803e3d6000fd5b505050506040513d6020811015612b7857600080fd5b50516009819055612b8f908263ffffffff613aeb16565b91505090565b600080821215612bd6576040805162461bcd60e51b8152602060048201526007602482015266534d493a4e454760c81b604482015290519081900360640190fd5b5090565b6000612be4610e37565b11612c28576040805162461bcd60e51b815260206004820152600f60248201526e4644543a5a45524f5f535550504c5960881b604482015290519081900360640190fd5b80612c32576110ed565b612c69612c3d610e37565b612c5183600160801b63ffffffff6123a516565b81612c5857fe5b60065491900463ffffffff61295116565b60065560408051828152905133917f26536799ace2c3dbe12e638ec3ade6b4173dcf1289be0a58d51a5003015649bd919081900360200190a260065460408051918252517f1f8d7705f31c3337a080803a8ad7e71946fb88d84738879be2bf402f97156e969181900360200190a150565b80600081121561094b576040805162461bcd60e51b815260206004820152600760248201526629a6aa9d27a7a160c91b604482015290519081900360640190fd5b6000828201818312801590612d305750838112155b80612d455750600083128015612d4557508381125b610eee5760405162461bcd60e51b8152600401808060200182810382526021815260200180613f5c6021913960400191505060405180910390fd5b600d54601054612d96908463ffffffff61295116565b6010558315612db657600f54612db2908563ffffffff61295116565b600f555b8015612e1457600c54612def907f0000000000000000000000000000000000000000000000000000000000278d0063ffffffff61295116565b600c558315612e0f57600e54612e0b908563ffffffff6126a216565b600e555b612f17565b6000600e819055600a805461ff001916610200179055600c556001600160a01b037f0000000000000000000000000cb4fb75e14358fb563cb815dc02e16fdf2a52181663f2d5d56b7f00000000000000000000000061be14477ca25571e197ecc6addb895c67618010612e8561339b565b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015612ed457600080fd5b505af1158015612ee8573d6000803e3d6000fd5b50505050612ef4612624565b60408051600281529051600080516020613eb18339815191529181900360200190a15b612f526001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481633308863ffffffff6131de16565b612f5a6110c2565b7fd0eb4a53827b5b6d9df4e56deb84ebbed98927c1d73f2468eef32f3c286d7a6085858584600e5460008711612f91576000612f95565b600c545b604080519687526020870195909552858501939093526060850191909152608084015260a083015284151560c0830152519081900360e00190a161177f613320565b600a5460ff1615613022576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b600a805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612ac36123fe565b6000816001600160a01b031663c31245256040518163ffffffff1660e01b815260040160206040518083038186803b15801561309357600080fd5b505afa158015612182573d6000803e3d6000fd5b336001600160a01b037f00000000000000000000000061be14477ca25571e197ecc6addb895c67618010161461106a576040805162461bcd60e51b815260206004820152600e60248201526d261d2727aa2fa127a92927aba2a960911b604482015290519081900360640190fd5b60007f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03166370a082317f000000000000000000000000058550063391330b10ab4c93d0762a767fdd2a4f6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156131ad57600080fd5b505afa1580156131c1573d6000803e3d6000fd5b505050506040513d60208110156131d757600080fd5b5051905090565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b17905261206d9085906138d3565b826001600160a01b031663f2d5d56b83836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561329857600080fd5b505af11580156132ac573d6000803e3d6000fd5b50505050505050565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03167f000000000000000000000000058550063391330b10ab4c93d0762a767fdd2a4f6001600160a01b0316600080516020613ef483398151915261268f613115565b604080516370a0823160e01b8152306004820181905291516001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169291600080516020613ef48339815191529184916370a08231916024808301926020929190829003018186803b158015610f8057600080fd5b60007f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5996001600160a01b03166370a082317f0000000000000000000000000cb4fb75e14358fb563cb815dc02e16fdf2a52186040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156131ad57600080fd5b6000336001600160a01b03166316f0115b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561346e57600080fd5b505afa158015613482573d6000803e3d6000fd5b505050506040513d602081101561349857600080fd5b5051604080516303526ce360e21b815290519192506000916001600160a01b03841691630d49b38c916004808301926020929190829003018186803b1580156134e057600080fd5b505afa1580156134f4573d6000803e3d6000fd5b505050506040513d602081101561350a57600080fd5b505190506135377f000000000000000000000000908cc851bc757248514e060ad8bd0a03908308ee613058565b6001600160a01b031663107c0240826040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561358c57600080fd5b505afa1580156135a0573d6000803e3d6000fd5b505050506040513d60208110156135b657600080fd5b505180156136425750806001600160a01b0316635b16ebb7836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561361557600080fd5b505afa158015613629573d6000803e3d6000fd5b505050506040513d602081101561363f57600080fd5b50515b61294d576040805162461bcd60e51b815260206004820152601060248201526f261d24a72b20a624a22fa622a72222a960811b604482015290519081900360640190fd5b6136d67f0000000000000000000000000000000000000000000000000000000060ed16c77f00000000000000000000000000000000000000000000000000000000000d2f0063ffffffff61295116565b42111561106a576040805162461bcd60e51b8152602060048201526015602482015274130e941054d517d1955391125391d7d411549253d1605a1b604482015290519081900360640190fd5b6000610a287f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561378057600080fd5b505afa158015613794573d6000803e3d6000fd5b505050506040513d60208110156137aa57600080fd5b5051600a0a611baf84670de0b6b3a764000063ffffffff6123a516565b6137d18282613bd5565b60006138136137ee61126f846006546123a590919063ffffffff16565b6001600160a01b0385166000908152600760205260409020549063ffffffff613aeb16565b6001600160a01b0384166000818152600760209081526040918290208490558151848152915193945091927ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773929181900390910190a2505050565b600081836138bd5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156128b657818101518382015260200161289e565b5060008385816138c957fe5b0495945050505050565b6060613928826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613cd19092919063ffffffff16565b80519091501561131e5780806020019051602081101561394757600080fd5b505161131e5760405162461bcd60e51b815260040180806020018281038252602a815260200180614059602a913960400191505060405180910390fd5b6001600160a01b0383166139c95760405162461bcd60e51b8152600401808060200182810382526025815260200180613fec6025913960400191505060405180910390fd5b6001600160a01b038216613a0e5760405162461bcd60e51b8152600401808060200182810382526023815260200180613ed16023913960400191505060405180910390fd5b613a1983838361131e565b613a5c81604051806060016040528060268152602001613f36602691396001600160a01b038616600090815260208190526040902054919063ffffffff61286216565b6001600160a01b038085166000908152602081905260408082209390935590841681522054613a91908263ffffffff61295116565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b6000818303818312801590613b005750838113155b80613b155750600083128015613b1557508381135b610eee5760405162461bcd60e51b81526004018080602001828103825260248152602001806140356024913960400191505060405180910390fd5b6000613b5b3361106c565b3360009081526008602052604081205491925090613b7f908363ffffffff61295116565b336000818152600860209081526040918290208490558151868152908101849052815193945091927ffbc3a599b784fe88772fc5abcc07223f64ca0b13acc341f4fb1e46bef0510eb49281900390910190a25090565b6001600160a01b038216613c30576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b613c3c6000838361131e565b600254613c4f908263ffffffff61295116565b6002556001600160a01b038216600090815260208190526040902054613c7b908263ffffffff61295116565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6060613ce08484600085613ce8565b949350505050565b606082471015613d295760405162461bcd60e51b8152600401808060200182810382526026815260200180613f7d6026913960400191505060405180910390fd5b613d3285613e44565b613d83576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310613dc25780518252601f199092019160209182019101613da3565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613e24576040519150601f19603f3d011682016040523d82523d6000602084013e613e29565b606091505b5091509150613e39828286613e4a565b979650505050505050565b3b151590565b60608315613e59575081610eee565b825115613e695782518084602001fd5b60405162461bcd60e51b81526020600482018181528451602484015284518593919283926044019190850190808383600083156128b657818101518382015260200161289e56fe400243eaf4da5ecbc2c6f2453605068a362c65ff9212fc60b58289b7e09d220945524332303a207472616e7366657220746f20746865207a65726f20616464726573732047d1633ff7768462ae07d28cb16e484203bfd6d85ce832494270ebcd9081a245524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63655369676e6564536166654d6174683a206164646974696f6e206f766572666c6f77416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f20616464726573735369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656445524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220d2590c75d517c40c26e5ced3921915b365df606e193d51a42f29b7cd42d4b47f64736f6c634300060b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000061be14477ca25571e197ecc6addb895c67618010000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5990000000000000000000000000eb96a53ec793a244876b018073f33b23000f25b000000000000000000000000ee3e59d381968f4f9c92460d9d5cfcf5d3a67987000000000000000000000000000000000000000000000000000000000000044c000000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000001d1a94a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007d622bb6ed13a599ec96366fa95f2452c64ce6020000000000000000000000008dc5aa328142aa8a008c25f66a77eaa8e4b46f3c000000000000000000000000e88ab4cf1ec06840d16fed69c964ad9daff5c6c2
-----Decoded View---------------
Arg [0] : _borrower (address): 0x61BE14477Ca25571E197eCC6Addb895c67618010
Arg [1] : _liquidityAsset (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [2] : _collateralAsset (address): 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599
Arg [3] : _flFactory (address): 0x0eB96A53EC793a244876b018073f33B23000F25b
Arg [4] : _clFactory (address): 0xEE3e59D381968f4F9C92460D9d5Cfcf5d3A67987
Arg [5] : specs (uint256[5]): 1100,90,30,2000000000000,0
Arg [6] : calcs (address[3]): 0x7d622bB6Ed13a599ec96366Fa95f2452c64ce602,0x8dC5aa328142aa8a008c25F66a77eaA8E4B46f3c,0xe88Ab4Cf1Ec06840d16feD69c964aD9DAFf5c6c2
-----Encoded View---------------
13 Constructor Arguments found :
Arg [0] : 00000000000000000000000061be14477ca25571e197ecc6addb895c67618010
Arg [1] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [2] : 0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599
Arg [3] : 0000000000000000000000000eb96a53ec793a244876b018073f33b23000f25b
Arg [4] : 000000000000000000000000ee3e59d381968f4f9c92460d9d5cfcf5d3a67987
Arg [5] : 000000000000000000000000000000000000000000000000000000000000044c
Arg [6] : 000000000000000000000000000000000000000000000000000000000000005a
Arg [7] : 000000000000000000000000000000000000000000000000000000000000001e
Arg [8] : 000000000000000000000000000000000000000000000000000001d1a94a2000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : 0000000000000000000000007d622bb6ed13a599ec96366fa95f2452c64ce602
Arg [11] : 0000000000000000000000008dc5aa328142aa8a008c25f66a77eaa8e4b46f3c
Arg [12] : 000000000000000000000000e88ab4cf1ec06840d16fed69c964ad9daff5c6c2
Libraries Used
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.