ETH Price: $2,505.60 (+0.57%)

Contract

0x2Cd79F7f8b38B9c0D80EA6B230441841A31537eC
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Create Pool154744892022-09-04 23:24:42784 days ago1662333882IN
0x2Cd79F7f...1A31537eC
0 ETH0.067993978.57856275
Create Pool153032552022-08-08 18:52:07812 days ago1659984727IN
0x2Cd79F7f...1A31537eC
0 ETH0.1921434624.24207244
Create Pool143390102022-03-07 9:56:25966 days ago1646646985IN
0x2Cd79F7f...1A31537eC
0 ETH0.1604004820.20469457
Create Pool142509602022-02-21 18:12:24980 days ago1645467144IN
0x2Cd79F7f...1A31537eC
0 ETH1.18782375150
Create Pool136201492021-11-15 11:45:381078 days ago1636976738IN
0x2Cd79F7f...1A31537eC
0 ETH0.87405263110
Create Pool135505632021-11-04 13:44:261089 days ago1636033466IN
0x2Cd79F7f...1A31537eC
0 ETH0.99065213124.67411187
Create Pool135063362021-10-28 14:44:241096 days ago1635432264IN
0x2Cd79F7f...1A31537eC
0 ETH0.83692973169.99413356
Create Pool127719882021-07-06 5:17:261210 days ago1625548646IN
0x2Cd79F7f...1A31537eC
0 ETH0.1191893515
Create Pool124623762021-05-19 3:15:091258 days ago1621394109IN
0x2Cd79F7f...1A31537eC
0 ETH1.15432474144.96
0x60806040124286082021-05-13 21:49:151263 days ago1620942555IN
 Create: PoolFactory
0 ETH0.64052256120

Latest 9 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
154744892022-09-04 23:24:42784 days ago1662333882
0x2Cd79F7f...1A31537eC
 Contract Creation0 ETH
153032552022-08-08 18:52:07812 days ago1659984727
0x2Cd79F7f...1A31537eC
 Contract Creation0 ETH
143390102022-03-07 9:56:25966 days ago1646646985
0x2Cd79F7f...1A31537eC
 Contract Creation0 ETH
142509602022-02-21 18:12:24980 days ago1645467144
0x2Cd79F7f...1A31537eC
 Contract Creation0 ETH
136201492021-11-15 11:45:381078 days ago1636976738
0x2Cd79F7f...1A31537eC
 Contract Creation0 ETH
135505632021-11-04 13:44:261089 days ago1636033466
0x2Cd79F7f...1A31537eC
 Contract Creation0 ETH
135063362021-10-28 14:44:241096 days ago1635432264
0x2Cd79F7f...1A31537eC
 Contract Creation0 ETH
127719882021-07-06 5:17:261210 days ago1625548646
0x2Cd79F7f...1A31537eC
 Contract Creation0 ETH
124623762021-05-19 3:15:091258 days ago1621394109
0x2Cd79F7f...1A31537eC
 Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PoolFactory

Compiler Version
v0.6.11+commit.5ef660b1

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, GNU AGPLv3 license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2021-05-13
*/

// SPDX-License-Identifier:  AGPL-3.0-or-later // hevm: flattened sources of contracts/PoolFactory.sol
pragma solidity =0.6.11 >=0.6.0 <0.8.0 >=0.6.2 <0.8.0;

////// contracts/interfaces/IBPool.sol
/* pragma solidity 0.6.11; */

interface IBPool {

    function transfer(address, uint256) external returns (bool);

    function INIT_POOL_SUPPLY() external view returns (uint256);

    function MAX_OUT_RATIO() external view returns (uint256);

    function bind(address, uint256, uint256) external;

    function balanceOf(address) external view returns (uint256);

    function finalize() external;

    function gulp(address) external;

    function isFinalized() external view returns (bool);

    function isBound(address) external view returns (bool);

    function getNumTokens() external view returns (uint256);

    function getBalance(address) external view returns (uint256);

    function getNormalizedWeight(address) external view returns (uint256);

    function getDenormalizedWeight(address) external view returns (uint256);

    function getTotalDenormalizedWeight() external view returns (uint256);

    function getSwapFee() external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function getFinalTokens() external view returns (address[] memory);

    function calcSingleOutGivenPoolIn(
        uint256 tokenBalanceOut,
        uint256 tokenWeightOut,
        uint256 poolSupply,
        uint256 totalWeight,
        uint256 poolAmountIn,
        uint256 swapFee
    ) external pure returns (uint256);

    function calcPoolInGivenSingleOut(
        uint256 tokenBalanceOut,
        uint256 tokenWeightOut,
        uint256 poolSupply,
        uint256 totalWeight,
        uint256 tokenAmountOut,
        uint256 swapFee
    ) external pure returns (uint256);

    function exitswapExternAmountOut(
        address tokenOut,
        uint256 tokenAmountOut,
        uint256 maxPoolAmountIn
    ) external returns (uint256 poolAmountIn);

}

////// contracts/interfaces/IDebtLocker.sol
/* pragma solidity 0.6.11; */

interface IDebtLocker {

    function loan() external view returns (address);

    function liquidityAsset() external view returns (address);

    function pool() external view returns (address);

    function lastPrincipalPaid() external view returns (uint256);

    function lastInterestPaid() external view returns (uint256);

    function lastFeePaid() external view returns (uint256);

    function lastExcessReturned() external view returns (uint256);

    function lastDefaultSuffered() external view returns (uint256);

    function lastAmountRecovered() external view returns (uint256);

    function claim() external returns (uint256[7] memory);
    
    function triggerDefault() external;

}

////// 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/ILiquidityLockerFactory.sol
/* pragma solidity 0.6.11; */

interface ILiquidityLockerFactory {

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

}

////// 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/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/ILoanFDT.sol
/* pragma solidity 0.6.11; */

/* import "./IBasicFDT.sol"; */

interface ILoanFDT is IBasicFDT {

    function fundsToken() external view returns (address);

    function fundsTokenBalance() external view returns (uint256);

}

////// contracts/interfaces/ILoan.sol
/* pragma solidity 0.6.11; */

/* import "../token/interfaces/ILoanFDT.sol"; */

interface ILoan is ILoanFDT {
    
    // State Variables
    function liquidityAsset() external view returns (address);
    
    function collateralAsset() external view returns (address);
    
    function fundingLocker() external view returns (address);
    
    function flFactory() external view returns (address);
    
    function collateralLocker() external view returns (address);
    
    function clFactory() external view returns (address);
    
    function borrower() external view returns (address);
    
    function repaymentCalc() external view returns (address);
    
    function lateFeeCalc() external view returns (address);
    
    function premiumCalc() external view returns (address);
    
    function loanState() external view returns (uint256);
    
    function collateralRequiredForDrawdown(uint256) external view returns (uint256);
    

    // Loan Specifications
    function apr() external view returns (uint256);
    
    function paymentsRemaining() external view returns (uint256);
    
    function paymentIntervalSeconds() external view returns (uint256);
    
    function requestAmount() external view returns (uint256);
    
    function collateralRatio() external view returns (uint256);
    
    function fundingPeriod() external view returns (uint256);

    function defaultGracePeriod() external view returns (uint256);
    
    function createdAt() external view returns (uint256);
    
    function principalOwed() external view returns (uint256);
    
    function principalPaid() external view returns (uint256);
    
    function interestPaid() external view returns (uint256);
    
    function feePaid() external view returns (uint256);
    
    function excessReturned() external view returns (uint256);
    
    function getNextPayment() external view returns (uint256, uint256, uint256, uint256);
    
    function superFactory() external view returns (address);
    
    function termDays() external view returns (uint256);
    
    function nextPaymentDue() external view returns (uint256);

    function getFullPayment() external view returns (uint256, uint256, uint256);
    

    // Liquidations
    function amountLiquidated() external view returns (uint256);

    function defaultSuffered() external view returns (uint256);
    
    function amountRecovered() external view returns (uint256);
    
    function getExpectedAmountRecovered() external view returns (uint256);

    function liquidationExcess() external view returns (uint256);
    

    // Functions
    function fundLoan(address, uint256) external;
    
    function makePayment() external;
    
    function drawdown(uint256) external;
    
    function makeFullPayment() external;
    
    function triggerDefault() external;
    
    function unwind() external;
    

    // Security 
    function pause() external;

    function unpause() external;

    function loanAdmins(address) external view returns (address);

    function setLoanAdmin(address, bool) external;


    // Misc
    function reclaimERC20(address) 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/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/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/IStakeLockerFDT.sol
/* pragma solidity 0.6.11; */

/* import "./IExtendedFDT.sol"; */

interface IStakeLockerFDT is IExtendedFDT {

    function fundsToken() external view returns (address);

    function fundsTokenBalance() external view returns (uint256);

    function bptLosses() external view returns (uint256);

    function lossesBalance() external view returns (uint256);

}

////// contracts/interfaces/IStakeLocker.sol
/* pragma solidity 0.6.11; */

/* import "../token/interfaces/IStakeLockerFDT.sol"; */

interface IStakeLocker is IStakeLockerFDT {

    function stakeDate(address) external returns (uint256);

    function stake(uint256) external;

    function unstake(uint256) external;

    function pull(address, uint256) external;

    function setAllowlist(address, bool) external;

    function openStakeLockerToPublic() external;

    function openToPublic() external view returns (bool);

    function allowed(address) external view returns (bool);

    function updateLosses(uint256) external;

    function intendToUnstake() external;

    function unstakeCooldown(address) external view returns (uint256);

    function lockupPeriod() external view returns (uint256);

    function stakeAsset() external view returns (address);

    function liquidityAsset() external view returns (address);

    function pool() external view returns (address);

    function setLockupPeriod(uint256) external;

    function cancelUnstake() external;

    function increaseCustodyAllowance(address, uint256) external;

    function transferByCustodian(address, address, uint256) external;

    function pause() external;

    function unpause() external;

    function isUnstakeAllowed(address) external view returns (bool);

    function isReceiveAllowed(uint256) external view returns (bool);

}

////// contracts/interfaces/IStakeLockerFactory.sol
/* pragma solidity 0.6.11; */

interface IStakeLockerFactory {

    function owner(address) external returns (address);

    function isLocker(address) external returns (bool);

    function factoryType() external returns (uint8);

    function newLocker(address, address) external returns (address);

}

////// contracts/interfaces/IDebtLockerFactory.sol
/* pragma solidity 0.6.11; */

interface IDebtLockerFactory {

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

}

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

////// 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/PoolLib.sol
/* pragma solidity 0.6.11; */

/* import "lib/openzeppelin-contracts/contracts/math/SafeMath.sol"; */
/* import "lib/openzeppelin-contracts/contracts/token/ERC20/SafeERC20.sol"; */
/* import "../interfaces/ILoan.sol"; */
/* import "../interfaces/IBPool.sol"; */
/* import "../interfaces/IMapleGlobals.sol"; */
/* import "../interfaces/ILiquidityLocker.sol"; */
/* import "../interfaces/IERC20Details.sol"; */
/* import "../interfaces/ILoanFactory.sol"; */
/* import "../interfaces/IStakeLocker.sol"; */
/* import "../interfaces/IDebtLockerFactory.sol"; */

/// @title PoolLib is a library of utility functions used by Pool.
library PoolLib {

    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    uint256 public constant MAX_UINT256 = uint256(-1);
    uint256 public constant WAD         = 10 ** 18;
    uint8   public constant DL_FACTORY  = 1;         // Factory type of DebtLockerFactory

    event         LoanFunded(address indexed loan, address debtLocker, uint256 amountFunded);
    event DepositDateUpdated(address indexed liquidityProvider, uint256 depositDate);

    /***************************************/
    /*** Pool Delegate Utility Functions ***/
    /***************************************/

    /** 
        @dev   Conducts sanity checks for Pools in the constructor.
        @param globals        Instance of a MapleGlobals.
        @param liquidityAsset Asset used by Pool for liquidity to fund loans.
        @param stakeAsset     Asset escrowed in StakeLocker.
        @param stakingFee     Fee that the Stakers earn on interest, in basis points.
        @param delegateFee    Fee that the Pool Delegate earns on interest, in basis points.
    */
    function poolSanityChecks(
        IMapleGlobals globals, 
        address liquidityAsset, 
        address stakeAsset, 
        uint256 stakingFee, 
        uint256 delegateFee
    ) external view {
        IBPool bPool = IBPool(stakeAsset);

        require(globals.isValidLiquidityAsset(liquidityAsset), "P:INVALID_LIQ_ASSET");
        require(stakingFee.add(delegateFee) <= 10_000,         "P:INVALID_FEES");
        require(
            globals.isValidBalancerPool(address(stakeAsset)) &&
            bPool.isBound(globals.mpl())                     && 
            bPool.isBound(liquidityAsset)                    &&
            bPool.isFinalized(), 
            "P:INVALID_BALANCER_POOL"
        );
    }

    /**
        @dev   Funds a Loan for an amount, utilizing the supplied DebtLockerFactory for DebtLockers.
        @dev   It emits a `LoanFunded` event.
        @param debtLockers     Mapping contains the DebtLocker contract address corresponding to the DebtLockerFactory and Loan.
        @param superFactory    Address of the PoolFactory.
        @param liquidityLocker Address of the LiquidityLocker contract attached with this Pool.
        @param loan            Address of the Loan to fund.
        @param dlFactory       The DebtLockerFactory to utilize.
        @param amt             Amount to fund the Loan.
    */
    function fundLoan(
        mapping(address => mapping(address => address)) storage debtLockers,
        address superFactory,
        address liquidityLocker,
        address loan,
        address dlFactory,
        uint256 amt
    ) external {
        IMapleGlobals globals = IMapleGlobals(ILoanFactory(superFactory).globals());
        address loanFactory   = ILoan(loan).superFactory();

        // Auth checks.
        require(globals.isValidLoanFactory(loanFactory),                        "P:INVALID_LF");
        require(ILoanFactory(loanFactory).isLoan(loan),                         "P:INVALID_L");
        require(globals.isValidSubFactory(superFactory, dlFactory, DL_FACTORY), "P:INVALID_DLF");

        address debtLocker = debtLockers[loan][dlFactory];

        // Instantiate DebtLocker if it doesn't exist withing this factory
        if (debtLocker == address(0)) {
            debtLocker = IDebtLockerFactory(dlFactory).newLocker(loan);
            debtLockers[loan][dlFactory] = debtLocker;
        }
    
        // Fund the Loan.
        ILiquidityLocker(liquidityLocker).fundLoan(loan, debtLocker, amt);
        
        emit LoanFunded(loan, debtLocker, amt);
    }

    /**
        @dev    Helper function used by Pool `claim` function, for when if a default has occurred.
        @param  liquidityAsset                  IERC20 of Liquidity Asset.
        @param  stakeLocker                     Address of StakeLocker.
        @param  stakeAsset                      Address of BPTs.
        @param  defaultSuffered                 Amount of shortfall in defaulted Loan after liquidation.
        @return bptsBurned                      Amount of BPTs burned to cover shortfall.
        @return postBurnBptBal                  Amount of BPTs returned to StakeLocker after burn.
        @return liquidityAssetRecoveredFromBurn Amount of Liquidity Asset recovered from burn.
    */
    function handleDefault(
        IERC20  liquidityAsset,
        address stakeLocker,
        address stakeAsset,
        uint256 defaultSuffered
    ) 
        external
        returns (
            uint256 bptsBurned,
            uint256 postBurnBptBal,
            uint256 liquidityAssetRecoveredFromBurn
        ) 
    {

        IBPool bPool = IBPool(stakeAsset);  // stakeAsset = Balancer Pool Tokens

        // Check amount of Liquidity Asset coverage that exists in the StakeLocker.
        uint256 availableSwapOut = getSwapOutValueLocker(stakeAsset, address(liquidityAsset), stakeLocker);

        // Pull BPTs from StakeLocker.
        IStakeLocker(stakeLocker).pull(address(this), bPool.balanceOf(stakeLocker));

        // To maintain accounting, account for direct transfers into Pool.
        uint256 preBurnLiquidityAssetBal = liquidityAsset.balanceOf(address(this));
        uint256 preBurnBptBal            = bPool.balanceOf(address(this));

        // Burn enough BPTs for Liquidity Asset to cover default suffered.
        bPool.exitswapExternAmountOut(
            address(liquidityAsset), 
            availableSwapOut >= defaultSuffered ? defaultSuffered : availableSwapOut,  // Burn BPTs up to defaultSuffered amount
            preBurnBptBal
        );

        // Return remaining BPTs to StakeLocker.
        postBurnBptBal = bPool.balanceOf(address(this));
        bptsBurned     = preBurnBptBal.sub(postBurnBptBal);
        bPool.transfer(stakeLocker, postBurnBptBal);
        liquidityAssetRecoveredFromBurn = liquidityAsset.balanceOf(address(this)).sub(preBurnLiquidityAssetBal);
        IStakeLocker(stakeLocker).updateLosses(bptsBurned);  // Update StakeLockerFDT loss accounting for BPTs
    }

    /**
        @dev    Calculates portions of claim from DebtLocker to be used by Pool `claim` function.
        @param  claimInfo           [0] = Total Claimed
                                    [1] = Interest Claimed
                                    [2] = Principal Claimed
                                    [3] = Fee Claimed
                                    [4] = Excess Returned Claimed
                                    [5] = Amount Recovered (from Liquidation)
                                    [6] = Default Suffered
        @param  delegateFee         Portion of interest (basis points) that goes to the Pool Delegate.
        @param  stakingFee          Portion of interest (basis points) that goes to the StakeLocker.
        @return poolDelegatePortion Total funds to send to the Pool Delegate.
        @return stakeLockerPortion  Total funds to send to the StakeLocker.
        @return principalClaim      Total principal claim.
        @return interestClaim       Total interest claim.
    */
    function calculateClaimAndPortions(
        uint256[7] calldata claimInfo,
        uint256 delegateFee,
        uint256 stakingFee
    ) 
        external
        pure
        returns (
            uint256 poolDelegatePortion,
            uint256 stakeLockerPortion,
            uint256 principalClaim,
            uint256 interestClaim
        ) 
    { 
        poolDelegatePortion = claimInfo[1].mul(delegateFee).div(10_000).add(claimInfo[3]);  // Pool Delegate portion of interest plus fee.
        stakeLockerPortion  = claimInfo[1].mul(stakingFee).div(10_000);                     // StakeLocker portion of interest.

        principalClaim = claimInfo[2].add(claimInfo[4]).add(claimInfo[5]);                                     // principal + excess + amountRecovered
        interestClaim  = claimInfo[1].sub(claimInfo[1].mul(delegateFee).div(10_000)).sub(stakeLockerPortion);  // leftover interest
    }

    /**
        @dev   Checks that the deactivation is allowed.
        @param globals        Instance of a MapleGlobals.
        @param principalOut   Amount of funds that are already funded to Loans.
        @param liquidityAsset Liquidity Asset of the Pool.
    */
    function validateDeactivation(IMapleGlobals globals, uint256 principalOut, address liquidityAsset) external view {
        require(principalOut <= _convertFromUsd(globals, liquidityAsset, 100), "P:PRINCIPAL_OUTSTANDING");
    }

    /********************************************/
    /*** Liquidity Provider Utility Functions ***/
    /********************************************/

    /**
        @dev   Updates the effective deposit date based on how much new capital has been added.
               If more capital is added, the deposit date moves closer to the current timestamp.
        @dev   It emits a `DepositDateUpdated` event.
        @param amt     Total deposit amount.
        @param account Address of account depositing.
    */
    function updateDepositDate(mapping(address => uint256) storage depositDate, uint256 balance, uint256 amt, address account) internal {
        uint256 prevDate = depositDate[account];

        // prevDate + (now - prevDate) * (amt / (balance + amt))
        // NOTE: prevDate = 0 implies balance = 0, and equation reduces to now
        uint256 newDate = (balance + amt) > 0
            ? prevDate.add(block.timestamp.sub(prevDate).mul(amt).div(balance + amt))
            : prevDate;

        depositDate[account] = newDate;
        emit DepositDateUpdated(account, newDate);
    }

    /**
        @dev Performs all necessary checks for a `transferByCustodian` call.
        @dev From and to must always be equal.
    */
    function transferByCustodianChecks(address from, address to, uint256 amount) external pure {
        require(to == from,                 "P:INVALID_RECEIVER");
        require(amount != uint256(0),       "P:INVALID_AMT");
    }

    /**
        @dev Performs all necessary checks for an `increaseCustodyAllowance` call.
    */
    function increaseCustodyAllowanceChecks(address custodian, uint256 amount, uint256 newTotalAllowance, uint256 fdtBal) external pure {
        require(custodian != address(0),     "P:INVALID_CUSTODIAN");
        require(amount    != uint256(0),     "P:INVALID_AMT");
        require(newTotalAllowance <= fdtBal, "P:INSUF_BALANCE");
    }

    /**********************************/
    /*** 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 Liquidity Asset that is supported by the Pool.
        @param globals        Instance of a MapleGlobals.
    */
    function reclaimERC20(address token, address liquidityAsset, IMapleGlobals globals) external {
        require(msg.sender == globals.governor(), "P:NOT_GOV");
        require(token != liquidityAsset && token != address(0), "P:INVALID_TOKEN");
        IERC20(token).safeTransfer(msg.sender, IERC20(token).balanceOf(address(this)));
    }

    /************************/
    /*** Getter Functions ***/
    /************************/

    /**
        @dev Official Balancer pool bdiv() function. Does synthetic float with 10^-18 precision.
    */
    function _bdiv(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0, "P:DIV_ZERO");
        uint256 c0 = a * WAD;
        require(a == 0 || c0 / a == WAD, "P:DIV_INTERNAL");  // bmul overflow
        uint256 c1 = c0 + (b / 2);
        require(c1 >= c0, "P:DIV_INTERNAL");  //  badd require
        return c1 / b;
    }

    /**
        @dev    Calculates the value of BPT in units of Liquidity Asset.
        @dev    Vulnerable to flash-loan attacks where the attacker can artificially inflate the BPT price by swapping a large amount
                of Liquidity Asset into the Pool and swapping back after this function is called.
        @param  _bPool         Address of Balancer pool.
        @param  liquidityAsset Asset used by Pool for liquidity to fund Loans.
        @param  staker         Address that deposited BPTs to StakeLocker.
        @param  stakeLocker    Escrows BPTs deposited by Staker.
        @return USDC value of Staker BPTs.
    */
    function BPTVal(
        address _bPool,
        address liquidityAsset,
        address staker,
        address stakeLocker
    ) external view returns (uint256) {
        IBPool bPool = IBPool(_bPool);

        // StakeLockerFDTs are minted 1:1 (in wei) in the StakeLocker when staking BPTs, thus representing stake amount.
        // These are burned when withdrawing staked BPTs, thus representing the current stake amount.
        uint256 amountStakedBPT       = IERC20(stakeLocker).balanceOf(staker);
        uint256 totalSupplyBPT        = IERC20(_bPool).totalSupply();
        uint256 liquidityAssetBalance = bPool.getBalance(liquidityAsset);
        uint256 liquidityAssetWeight  = bPool.getNormalizedWeight(liquidityAsset);

        // liquidityAsset value = (amountStaked/totalSupply) * (liquidityAssetBalance/liquidityAssetWeight)
        return _bdiv(amountStakedBPT, totalSupplyBPT).mul(_bdiv(liquidityAssetBalance, liquidityAssetWeight)).div(WAD);
    }

    /** 
        @dev    Calculates Liquidity Asset swap out value of staker BPT balance escrowed in StakeLocker.
        @param  _bPool         Balancer pool that issues the BPTs.
        @param  liquidityAsset Swap out asset (e.g. USDC) to receive when burning BPTs.
        @param  staker         Address that deposited BPTs to StakeLocker.
        @param  stakeLocker    Escrows BPTs deposited by Staker.
        @return liquidityAsset Swap out value of staker BPTs.
    */
    function getSwapOutValue(
        address _bPool,
        address liquidityAsset,
        address staker,
        address stakeLocker
    ) public view returns (uint256) {
        return _getSwapOutValue(_bPool, liquidityAsset, IERC20(stakeLocker).balanceOf(staker));
    }

    /** 
        @dev    Calculates Liquidity Asset swap out value of entire BPT balance escrowed in StakeLocker.
        @param  _bPool         Balancer pool that issues the BPTs.
        @param  liquidityAsset Swap out asset (e.g. USDC) to receive when burning BPTs.
        @param  stakeLocker    Escrows BPTs deposited by Staker.
        @return liquidityAsset Swap out value of StakeLocker BPTs.
    */
    function getSwapOutValueLocker(
        address _bPool,
        address liquidityAsset,
        address stakeLocker
    ) public view returns (uint256) {
        return _getSwapOutValue(_bPool, liquidityAsset, IBPool(_bPool).balanceOf(stakeLocker));
    }

    function _getSwapOutValue(
        address _bPool,
        address liquidityAsset,
        uint256 poolAmountIn
    ) internal view returns (uint256) {
        // Fetch Balancer pool token information
        IBPool bPool            = IBPool(_bPool);
        uint256 tokenBalanceOut = bPool.getBalance(liquidityAsset);
        uint256 tokenWeightOut  = bPool.getDenormalizedWeight(liquidityAsset);
        uint256 poolSupply      = bPool.totalSupply();
        uint256 totalWeight     = bPool.getTotalDenormalizedWeight();
        uint256 swapFee         = bPool.getSwapFee();

        // Returns the amount of liquidityAsset that can be recovered from BPT burning
        uint256 tokenAmountOut = bPool.calcSingleOutGivenPoolIn(
            tokenBalanceOut,
            tokenWeightOut,
            poolSupply,
            totalWeight,
            poolAmountIn,
            swapFee
        );

        // Max amount that can be swapped based on amount of liquidityAsset in the Balancer Pool
        uint256 maxSwapOut = tokenBalanceOut.mul(bPool.MAX_OUT_RATIO()).div(WAD);  

        return tokenAmountOut <= maxSwapOut ? tokenAmountOut : maxSwapOut;
    }

    /**
        @dev    Calculates BPTs required if burning BPTs for liquidityAsset, given supplied tokenAmountOutRequired.
        @dev    Vulnerable to flash-loan attacks where the attacker can artificially inflate the BPT price by swapping a large amount
                of liquidityAsset into the pool and swapping back after this function is called.
        @param  _bPool                       Balancer pool that issues the BPTs.
        @param  liquidityAsset               Swap out asset (e.g. USDC) to receive when burning BPTs.
        @param  staker                       Address that deposited BPTs to stakeLocker.
        @param  stakeLocker                  Escrows BPTs deposited by staker.
        @param  liquidityAssetAmountRequired Amount of liquidityAsset required to recover.
        @return poolAmountInRequired         poolAmountIn required.
        @return stakerBalance                poolAmountIn currently staked.
    */
    function getPoolSharesRequired(
        address _bPool,
        address liquidityAsset,
        address staker,
        address stakeLocker,
        uint256 liquidityAssetAmountRequired
    ) public view returns (uint256 poolAmountInRequired, uint256 stakerBalance) {
        // Fetch Balancer pool token information.
        IBPool bPool = IBPool(_bPool);

        uint256 tokenBalanceOut = bPool.getBalance(liquidityAsset);
        uint256 tokenWeightOut  = bPool.getDenormalizedWeight(liquidityAsset);
        uint256 poolSupply      = bPool.totalSupply();
        uint256 totalWeight     = bPool.getTotalDenormalizedWeight();
        uint256 swapFee         = bPool.getSwapFee();

        // Fetch amount of BPTs required to burn to receive Liquidity Asset amount required.
        poolAmountInRequired = bPool.calcPoolInGivenSingleOut(
            tokenBalanceOut,
            tokenWeightOut,
            poolSupply,
            totalWeight,
            liquidityAssetAmountRequired,
            swapFee
        );

        // Fetch amount staked in StakeLocker by Staker.
        stakerBalance = IERC20(stakeLocker).balanceOf(staker);
    }

    /**
        @dev    Returns information on the stake requirements.
        @param  globals                    Instance of a MapleGlobals.
        @param  balancerPool               Address of Balancer pool.
        @param  liquidityAsset             Address of Liquidity Asset, to be returned from swap out.
        @param  poolDelegate               Address of Pool Delegate.
        @param  stakeLocker                Address of StakeLocker.
        @return swapOutAmountRequired      Min amount of Liquidity Asset coverage from staking required (in Liquidity Asset units).
        @return currentPoolDelegateCover   Present amount of Liquidity Asset coverage from Pool Delegate stake (in Liquidity Asset units).
        @return enoughStakeForFinalization If enough stake is present from Pool Delegate for Pool finalization.
        @return poolAmountInRequired       BPTs required for minimum Liquidity Asset coverage.
        @return poolAmountPresent          Current staked BPTs.
    */
    function getInitialStakeRequirements(IMapleGlobals globals, address balancerPool, address liquidityAsset, address poolDelegate, address stakeLocker) external view returns (
        uint256 swapOutAmountRequired,
        uint256 currentPoolDelegateCover,
        bool    enoughStakeForFinalization,
        uint256 poolAmountInRequired,
        uint256 poolAmountPresent
    ) {
        swapOutAmountRequired = _convertFromUsd(globals, liquidityAsset, globals.swapOutRequired());
        (
            poolAmountInRequired,
            poolAmountPresent
        ) = getPoolSharesRequired(balancerPool, liquidityAsset, poolDelegate, stakeLocker, swapOutAmountRequired);

        currentPoolDelegateCover   = getSwapOutValue(balancerPool, liquidityAsset, poolDelegate, stakeLocker);
        enoughStakeForFinalization = poolAmountPresent >= poolAmountInRequired;
    }

    /************************/
    /*** Helper Functions ***/
    /************************/

    /**
        @dev   Converts from WAD precision to Liquidity Asset precision.
        @param amt                    Amount to convert.
        @param liquidityAssetDecimals Liquidity Asset decimal.
    */
    function fromWad(uint256 amt, uint256 liquidityAssetDecimals) external pure returns (uint256) {
        return amt.mul(10 ** liquidityAssetDecimals).div(WAD);
    }

    /** 
        @dev    Returns Liquidity Asset in Liquidity Asset units when given integer USD (E.g., $100 = 100).
        @param  globals        Instance of a MapleGlobals.
        @param  liquidityAsset Liquidity Asset of the pool.
        @param  usdAmount      USD amount to convert, in integer units (e.g., $100 = 100).
        @return usdAmount worth of Liquidity Asset, in Liquidity Asset units.
    */
    function _convertFromUsd(IMapleGlobals globals, address liquidityAsset, uint256 usdAmount) internal view returns (uint256) {
        return usdAmount
            .mul(10 ** 8)                                         // Cancel out 10 ** 8 decimals from oracle.
            .mul(10 ** IERC20Details(liquidityAsset).decimals())  // Convert to Liquidity Asset precision.
            .div(globals.getLatestPrice(liquidityAsset));         // Convert to Liquidity Asset value.
    }
}

////// 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/ExtendedFDT.sol
/* pragma solidity 0.6.11; */

/* import "./BasicFDT.sol"; */

/// @title ExtendedFDT implements FDT functionality for accounting for losses.
abstract contract ExtendedFDT is BasicFDT {
    using SafeMath       for uint256;
    using SafeMathUint   for uint256;
    using SignedSafeMath for  int256;
    using SafeMathInt    for  int256;

    uint256 internal lossesPerShare;

    mapping(address => int256)  internal lossesCorrection;
    mapping(address => uint256) internal recognizedLosses;

    event   LossesPerShareUpdated(uint256 lossesPerShare);
    event LossesCorrectionUpdated(address indexed account, int256 lossesCorrection);

    /**
        @dev   This event emits when new losses are distributed.
        @param by                The address of the account that has distributed losses.
        @param lossesDistributed The amount of losses received for distribution.
    */
    event LossesDistributed(address indexed by, uint256 lossesDistributed);

    /**
        @dev   This event emits when distributed losses are recognized by a token holder.
        @param by                    The address of the receiver of losses.
        @param lossesRecognized      The amount of losses that were recognized.
        @param totalLossesRecognized The total amount of losses that are recognized.
    */
    event LossesRecognized(address indexed by, uint256 lossesRecognized, uint256 totalLossesRecognized);

    constructor(string memory name, string memory symbol) BasicFDT(name, symbol) public { }

    /**
        @dev Distributes losses to token holders.
        @dev It reverts if the total supply of tokens is 0.
        @dev It emits a `LossesDistributed` event if the amount of received losses is greater than 0.
        @dev It emits a `LossesPerShareUpdated` event if the amount of received losses is greater than 0.
             About undistributed losses:
                In each distribution, there is a small amount of losses which do not get distributed,
                which is `(value * pointsMultiplier) % totalSupply()`.
             With a well-chosen `pointsMultiplier`, the amount losses that are not getting distributed
                in a distribution can be less than 1 (base unit).
             We can actually keep track of the undistributed losses in a distribution
                and try to distribute it in the next distribution.
    */
    function _distributeLosses(uint256 value) internal {
        require(totalSupply() > 0, "FDT:ZERO_SUPPLY");

        if (value == 0) return;

        uint256 _lossesPerShare = lossesPerShare.add(value.mul(pointsMultiplier) / totalSupply());
        lossesPerShare          = _lossesPerShare;

        emit LossesDistributed(msg.sender, value);
        emit LossesPerShareUpdated(_lossesPerShare);
    }

    /**
        @dev    Prepares losses for a withdrawal.
        @dev    It emits a `LossesWithdrawn` event if the amount of withdrawn losses is greater than 0.
        @return recognizableDividend The amount of dividend losses that can be recognized.
    */
    function _prepareLossesWithdraw() internal returns (uint256 recognizableDividend) {
        recognizableDividend = recognizableLossesOf(msg.sender);

        uint256 _recognizedLosses    = recognizedLosses[msg.sender].add(recognizableDividend);
        recognizedLosses[msg.sender] = _recognizedLosses;

        emit LossesRecognized(msg.sender, recognizableDividend, _recognizedLosses);
    }

    /**
        @dev    Returns the amount of losses that an address can withdraw.
        @param  _owner The address of a token holder.
        @return The amount of losses that `_owner` can withdraw.
    */
    function recognizableLossesOf(address _owner) public view returns (uint256) {
        return accumulativeLossesOf(_owner).sub(recognizedLosses[_owner]);
    }

    /**
        @dev    Returns the amount of losses that an address has recognized.
        @param  _owner The address of a token holder
        @return The amount of losses that `_owner` has recognized
    */
    function recognizedLossesOf(address _owner) external view returns (uint256) {
        return recognizedLosses[_owner];
    }

    /**
        @dev    Returns the amount of losses that an address has earned in total.
        @dev    accumulativeLossesOf(_owner) = recognizableLossesOf(_owner) + recognizedLossesOf(_owner)
                = (lossesPerShare * balanceOf(_owner) + lossesCorrection[_owner]) / pointsMultiplier
        @param  _owner The address of a token holder
        @return The amount of losses that `_owner` has earned in total
    */
    function accumulativeLossesOf(address _owner) public view returns (uint256) {
        return
            lossesPerShare
                .mul(balanceOf(_owner))
                .toInt256Safe()
                .add(lossesCorrection[_owner])
                .toUint256Safe() / pointsMultiplier;
    }

    /**
        @dev   Transfers tokens from one account to another. Updates pointsCorrection to keep funds unchanged.
        @dev         It emits two `LossesCorrectionUpdated` 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 _lossesCorrection    = lossesPerShare.mul(value).toInt256Safe();
        int256 lossesCorrectionFrom = lossesCorrection[from].add(_lossesCorrection);
        lossesCorrection[from]      = lossesCorrectionFrom;
        int256 lossesCorrectionTo   = lossesCorrection[to].sub(_lossesCorrection);
        lossesCorrection[to]        = lossesCorrectionTo;

        emit LossesCorrectionUpdated(from, lossesCorrectionFrom);
        emit LossesCorrectionUpdated(to,   lossesCorrectionTo);
    }

    /**
        @dev   Mints tokens to an account. Updates lossesCorrection to keep losses unchanged.
        @dev   It emits a `LossesCorrectionUpdated` event.
        @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 _lossesCorrection = lossesCorrection[account].sub(
            (lossesPerShare.mul(value)).toInt256Safe()
        );

        lossesCorrection[account] = _lossesCorrection;

        emit LossesCorrectionUpdated(account, _lossesCorrection);
    }

    /**
        @dev   Burns an amount of the token of a given account. Updates lossesCorrection to keep losses unchanged.
        @dev   It emits a `LossesCorrectionUpdated` event.
        @param account The account from which 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 _lossesCorrection = lossesCorrection[account].add(
            (lossesPerShare.mul(value)).toInt256Safe()
        );

        lossesCorrection[account] = _lossesCorrection;

        emit LossesCorrectionUpdated(account, _lossesCorrection);
    }

    /**
        @dev Registers a loss. May be called directly after a shortfall after BPT burning occurs.
        @dev Calls _updateLossesTokenBalance(), whereby the contract computes the delta of the new and previous
             losses balance and increments the total losses (cumulative), by delta, by calling _distributeLosses().
    */
    function updateLossesReceived() public virtual {
        int256 newLosses = _updateLossesBalance();

        if (newLosses <= 0) return;

        _distributeLosses(newLosses.toUint256Safe());
    }

    /**
        @dev Recognizes all recognizable losses for an account using loss accounting.
    */
    function _recognizeLosses() internal virtual returns (uint256 losses) { }

    /**
        @dev    Updates the current losses balance and returns the difference of the new and previous losses balance.
        @return A int256 representing the difference of the new and previous losses balance.
    */
    function _updateLossesBalance() internal virtual returns (int256) { }
}

////// contracts/token/PoolFDT.sol
/* pragma solidity 0.6.11; */

/* import "./ExtendedFDT.sol"; */

/// @title PoolFDT inherits ExtendedFDT and accounts for gains/losses for Liquidity Providers.
abstract contract PoolFDT is ExtendedFDT {
    using SafeMath       for uint256;
    using SafeMathUint   for uint256;
    using SignedSafeMath for  int256;
    using SafeMathInt    for  int256;

    uint256 public interestSum;  // Sum of all withdrawable interest.
    uint256 public poolLosses;   // Sum of all unrecognized losses.

    uint256 public interestBalance;  // The amount of earned interest present and accounted for in this contract.
    uint256 public lossesBalance;    // The amount of losses present and accounted for in this contract.

    constructor(string memory name, string memory symbol) ExtendedFDT(name, symbol) public { }

    /**
        @dev Realizes losses incurred to LPs.
    */
    function _recognizeLosses() internal override returns (uint256 losses) {
        losses = _prepareLossesWithdraw();

        poolLosses = poolLosses.sub(losses);

        _updateLossesBalance();
    }

    /**
        @dev    Updates the current losses balance and returns the difference of the new and previous losses balance.
        @return A int256 representing the difference of the new and previous losses balance.
    */
    function _updateLossesBalance() internal override returns (int256) {
        uint256 _prevLossesTokenBalance = lossesBalance;

        lossesBalance = poolLosses;

        return int256(lossesBalance).sub(int256(_prevLossesTokenBalance));
    }

    /**
        @dev    Updates the current interest balance and returns the difference of the new and previous interest balance.
        @return A int256 representing the difference of the new and previous interest balance.
    */
    function _updateFundsTokenBalance() internal override returns (int256) {
        uint256 _prevFundsTokenBalance = interestBalance;

        interestBalance = interestSum;

        return int256(interestBalance).sub(int256(_prevFundsTokenBalance));
    }
}

////// contracts/Pool.sol
/* pragma solidity 0.6.11; */

/* import "lib/openzeppelin-contracts/contracts/token/ERC20/SafeERC20.sol"; */

/* import "./interfaces/IBPool.sol"; */
/* import "./interfaces/IDebtLocker.sol"; */
/* import "./interfaces/IMapleGlobals.sol"; */
/* import "./interfaces/ILiquidityLocker.sol"; */
/* import "./interfaces/ILiquidityLockerFactory.sol"; */
/* import "./interfaces/ILoan.sol"; */
/* import "./interfaces/ILoanFactory.sol"; */
/* import "./interfaces/IPoolFactory.sol"; */
/* import "./interfaces/IStakeLocker.sol"; */
/* import "./interfaces/IStakeLockerFactory.sol"; */

/* import "./library/PoolLib.sol"; */

/* import "./token/PoolFDT.sol"; */

/// @title Pool maintains all accounting and functionality related to Pools.
contract Pool is PoolFDT {

    using SafeMath  for uint256;
    using SafeERC20 for IERC20;

    uint256 constant WAD = 10 ** 18;

    uint8 public constant DL_FACTORY = 1;  // Factory type of `DebtLockerFactory`.

    IERC20  public immutable liquidityAsset;  // The asset deposited by Lenders into the LiquidityLocker, for funding Loans.

    address public immutable poolDelegate;     // The Pool Delegate address, maintains full authority over the Pool.
    address public immutable liquidityLocker;  // The LiquidityLocker owned by this contract
    address public immutable stakeAsset;       // The address of the asset deposited by Stakers into the StakeLocker (BPTs), for liquidation during default events.
    address public immutable stakeLocker;      // The address of the StakeLocker, escrowing `stakeAsset`.
    address public immutable superFactory;     // The factory that deployed this Loan.

    uint256 private immutable liquidityAssetDecimals;  // The precision for the Liquidity Asset (i.e. `decimals()`).

    uint256 public           stakingFee;   // The fee Stakers earn            (in basis points).
    uint256 public immutable delegateFee;  // The fee the Pool Delegate earns (in basis points).

    uint256 public principalOut;  // The sum of all outstanding principal on Loans.
    uint256 public liquidityCap;  // The amount of liquidity tokens accepted by the Pool.
    uint256 public lockupPeriod;  // The period of time from an account's deposit date during which they cannot withdraw any funds.

    bool public openToPublic;  // Boolean opening Pool to public for LP deposits

    enum State { Initialized, Finalized, Deactivated }
    State public poolState;

    mapping(address => uint256)                     public depositDate;                // Used for withdraw penalty calculation.
    mapping(address => mapping(address => address)) public debtLockers;                // Address of the DebtLocker corresponding to `[Loan][DebtLockerFactory]`.
    mapping(address => bool)                        public poolAdmins;                 // The Pool Admin addresses that have permission to do certain operations in case of disaster management.
    mapping(address => bool)                        public allowedLiquidityProviders;  // Mapping that contains the list of addresses that have early access to the pool.
    mapping(address => uint256)                     public withdrawCooldown;           // The timestamp of when individual LPs have notified of their intent to withdraw.
    mapping(address => mapping(address => uint256)) public custodyAllowance;           // The amount of PoolFDTs that are "locked" at a certain address.
    mapping(address => uint256)                     public totalCustodyAllowance;      // The total amount of PoolFDTs that are "locked" for a given account. Cannot be greater than an account's balance.

    event                   LoanFunded(address indexed loan, address debtLocker, uint256 amountFunded);
    event                        Claim(address indexed loan, uint256 interest, uint256 principal, uint256 fee, uint256 stakeLockerPortion, uint256 poolDelegatePortion);
    event               BalanceUpdated(address indexed liquidityProvider, address indexed token, uint256 balance);
    event              CustodyTransfer(address indexed custodian, address indexed from, address indexed to, uint256 amount);
    event      CustodyAllowanceChanged(address indexed liquidityProvider, address indexed custodian, uint256 oldAllowance, uint256 newAllowance);
    event              LPStatusChanged(address indexed liquidityProvider, bool status);
    event              LiquidityCapSet(uint256 newLiquidityCap);
    event              LockupPeriodSet(uint256 newLockupPeriod);
    event                StakingFeeSet(uint256 newStakingFee);
    event             PoolStateChanged(State state);
    event                     Cooldown(address indexed liquidityProvider, uint256 cooldown);
    event           PoolOpenedToPublic(bool isOpen);
    event                 PoolAdminSet(address indexed poolAdmin, bool allowed);
    event           DepositDateUpdated(address indexed liquidityProvider, uint256 depositDate);
    event TotalCustodyAllowanceUpdated(address indexed liquidityProvider, uint256 newTotalAllowance);
    
    event DefaultSuffered(
        address indexed loan,
        uint256 defaultSuffered,
        uint256 bptsBurned,
        uint256 bptsReturned,
        uint256 liquidityAssetRecoveredFromBurn
    );

    /**
        Universal accounting law:
                                       fdtTotalSupply = liquidityLockerBal + principalOut - interestSum + poolLosses
            fdtTotalSupply + interestSum - poolLosses = liquidityLockerBal + principalOut
    */

    /**
        @dev   Constructor for a Pool.
        @dev   It emits a `PoolStateChanged` event.
        @param _poolDelegate   Address that has manager privileges of the Pool.
        @param _liquidityAsset Asset used to fund the Pool, It gets escrowed in LiquidityLocker.
        @param _stakeAsset     Asset escrowed in StakeLocker.
        @param _slFactory      Factory used to instantiate the StakeLocker.
        @param _llFactory      Factory used to instantiate the LiquidityLocker.
        @param _stakingFee     Fee that Stakers earn on interest, in basis points.
        @param _delegateFee    Fee that the Pool Delegate earns on interest, in basis points.
        @param _liquidityCap   Max amount of Liquidity Asset accepted by the Pool.
        @param name            Name of Pool token.
        @param symbol          Symbol of Pool token.
    */
    constructor(
        address _poolDelegate,
        address _liquidityAsset,
        address _stakeAsset,
        address _slFactory,
        address _llFactory,
        uint256 _stakingFee,
        uint256 _delegateFee,
        uint256 _liquidityCap,
        string memory name,
        string memory symbol
    ) PoolFDT(name, symbol) public {

        // Conduct sanity checks on Pool parameters.
        PoolLib.poolSanityChecks(_globals(msg.sender), _liquidityAsset, _stakeAsset, _stakingFee, _delegateFee);

        // Assign variables relating to the Liquidity Asset.
        liquidityAsset         = IERC20(_liquidityAsset);
        liquidityAssetDecimals = ERC20(_liquidityAsset).decimals();

        // Assign state variables.
        stakeAsset   = _stakeAsset;
        poolDelegate = _poolDelegate;
        stakingFee   = _stakingFee;
        delegateFee  = _delegateFee;
        superFactory = msg.sender;
        liquidityCap = _liquidityCap;

        // Instantiate the LiquidityLocker and the StakeLocker.
        stakeLocker     = address(IStakeLockerFactory(_slFactory).newLocker(_stakeAsset, _liquidityAsset));
        liquidityLocker = address(ILiquidityLockerFactory(_llFactory).newLocker(_liquidityAsset));

        lockupPeriod = 180 days;

        emit PoolStateChanged(State.Initialized);
    }

    /*******************************/
    /*** Pool Delegate Functions ***/
    /*******************************/

    /**
        @dev Finalizes the Pool, enabling deposits. Checks the amount the Pool Delegate deposited to the StakeLocker.
             Only the Pool Delegate can call this function.
        @dev It emits a `PoolStateChanged` event.
    */
    function finalize() external {
        _isValidDelegateAndProtocolNotPaused();
        _isValidState(State.Initialized);
        (,, bool stakeSufficient,,) = getInitialStakeRequirements();
        require(stakeSufficient, "P:INSUF_STAKE");
        poolState = State.Finalized;
        emit PoolStateChanged(poolState);
    }

    /**
        @dev   Funds a Loan for an amount, utilizing the supplied DebtLockerFactory for DebtLockers.
               Only the Pool Delegate can call this function.
        @dev   It emits a `LoanFunded` event.
        @dev   It emits a `BalanceUpdated` event.
        @param loan      Address of the Loan to fund.
        @param dlFactory Address of the DebtLockerFactory to utilize.
        @param amt       Amount to fund the Loan.
    */
    function fundLoan(address loan, address dlFactory, uint256 amt) external {
        _isValidDelegateAndProtocolNotPaused();
        _isValidState(State.Finalized);
        principalOut = principalOut.add(amt);
        PoolLib.fundLoan(debtLockers, superFactory, liquidityLocker, loan, dlFactory, amt);
        _emitBalanceUpdatedEvent();
    }

    /**
        @dev   Liquidates a Loan. The Pool Delegate could liquidate the Loan only when the Loan completes its grace period.
               The Pool Delegate can claim its proportion of recovered funds from the liquidation using the `claim()` function.
               Only the Pool Delegate can call this function.
        @param loan      Address of the Loan to liquidate.
        @param dlFactory Address of the DebtLockerFactory that is used to pull corresponding DebtLocker.
    */
    function triggerDefault(address loan, address dlFactory) external {
        _isValidDelegateAndProtocolNotPaused();
        IDebtLocker(debtLockers[loan][dlFactory]).triggerDefault();
    }

    /**
        @dev    Claims available funds for the Loan through a specified DebtLockerFactory. Only the Pool Delegate or a Pool Admin can call this function.
        @dev    It emits two `BalanceUpdated` events.
        @dev    It emits a `Claim` event.
        @param  loan      Address of the loan to claim from.
        @param  dlFactory Address of the DebtLockerFactory.
        @return claimInfo The claim details.
                    claimInfo [0] = Total amount claimed
                    claimInfo [1] = Interest  portion claimed
                    claimInfo [2] = Principal portion claimed
                    claimInfo [3] = Fee       portion claimed
                    claimInfo [4] = Excess    portion claimed
                    claimInfo [5] = Recovered portion claimed (from liquidations)
                    claimInfo [6] = Default suffered
    */
    function claim(address loan, address dlFactory) external returns (uint256[7] memory claimInfo) {
        _whenProtocolNotPaused();
        _isValidDelegateOrPoolAdmin();
        claimInfo = IDebtLocker(debtLockers[loan][dlFactory]).claim();

        (uint256 poolDelegatePortion, uint256 stakeLockerPortion, uint256 principalClaim, uint256 interestClaim) = PoolLib.calculateClaimAndPortions(claimInfo, delegateFee, stakingFee);

        // Subtract outstanding principal by the principal claimed plus excess returned.
        // Considers possible `principalClaim` overflow if Liquidity Asset is transferred directly into the Loan.
        if (principalClaim <= principalOut) {
            principalOut = principalOut - principalClaim;
        } else {
            interestClaim  = interestClaim.add(principalClaim - principalOut);  // Distribute `principalClaim` overflow as interest to LPs.
            principalClaim = principalOut;                                      // Set `principalClaim` to `principalOut` so correct amount gets transferred.
            principalOut   = 0;                                                 // Set `principalOut` to zero to avoid subtraction overflow.
        }

        // Accounts for rounding error in StakeLocker / Pool Delegate / LiquidityLocker interest split.
        interestSum = interestSum.add(interestClaim);

        _transferLiquidityAsset(poolDelegate, poolDelegatePortion);  // Transfer the fee and portion of interest to the Pool Delegate.
        _transferLiquidityAsset(stakeLocker,  stakeLockerPortion);   // Transfer the portion of interest to the StakeLocker.

        // Transfer remaining claim (remaining interest + principal + excess + recovered) to the LiquidityLocker.
        // Dust will accrue in the Pool, but this ensures that state variables are in sync with the LiquidityLocker balance updates.
        // Not using `balanceOf` in case of external address transferring the Liquidity Asset directly into Pool.
        // Ensures that internal accounting is exactly reflective of balance change.
        _transferLiquidityAsset(liquidityLocker, principalClaim.add(interestClaim));

        // Handle default if defaultSuffered > 0.
        if (claimInfo[6] > 0) _handleDefault(loan, claimInfo[6]);

        // Update funds received for StakeLockerFDTs.
        IStakeLocker(stakeLocker).updateFundsReceived();

        // Update funds received for PoolFDTs.
        updateFundsReceived();

        _emitBalanceUpdatedEvent();
        emit BalanceUpdated(stakeLocker, address(liquidityAsset), liquidityAsset.balanceOf(stakeLocker));

        emit Claim(loan, interestClaim, principalClaim, claimInfo[3], stakeLockerPortion, poolDelegatePortion);
    }

    /**
        @dev   Handles if a claim has been made and there is a non-zero defaultSuffered amount.
        @dev   It emits a `DefaultSuffered` event.
        @param loan            Address of a Loan that has defaulted.
        @param defaultSuffered Losses suffered from default after liquidation.
    */
    function _handleDefault(address loan, uint256 defaultSuffered) internal {

        (uint256 bptsBurned, uint256 postBurnBptBal, uint256 liquidityAssetRecoveredFromBurn) = PoolLib.handleDefault(liquidityAsset, stakeLocker, stakeAsset, defaultSuffered);

        // If BPT burn is not enough to cover full default amount, pass on losses to LPs with PoolFDT loss accounting.
        if (defaultSuffered > liquidityAssetRecoveredFromBurn) {
            poolLosses = poolLosses.add(defaultSuffered - liquidityAssetRecoveredFromBurn);
            updateLossesReceived();
        }

        // Transfer Liquidity Asset from burn to LiquidityLocker.
        liquidityAsset.safeTransfer(liquidityLocker, liquidityAssetRecoveredFromBurn);

        principalOut = principalOut.sub(defaultSuffered);  // Subtract rest of the Loan's principal from `principalOut`.

        emit DefaultSuffered(
            loan,                            // The Loan that suffered the default.
            defaultSuffered,                 // Total default suffered from the Loan by the Pool after liquidation.
            bptsBurned,                      // Amount of BPTs burned from StakeLocker.
            postBurnBptBal,                  // Remaining BPTs in StakeLocker post-burn.
            liquidityAssetRecoveredFromBurn  // Amount of Liquidity Asset recovered from burning BPTs.
        );
    }

    /**
        @dev Triggers deactivation, permanently shutting down the Pool. Must have less than 100 USD worth of Liquidity Asset `principalOut`.
             Only the Pool Delegate can call this function.
        @dev It emits a `PoolStateChanged` event.
    */
    function deactivate() external {
        _isValidDelegateAndProtocolNotPaused();
        _isValidState(State.Finalized);
        PoolLib.validateDeactivation(_globals(superFactory), principalOut, address(liquidityAsset));
        poolState = State.Deactivated;
        emit PoolStateChanged(poolState);
    }

    /**************************************/
    /*** Pool Delegate Setter Functions ***/
    /**************************************/

    /**
        @dev   Sets the liquidity cap. Only the Pool Delegate or a Pool Admin can call this function.
        @dev   It emits a `LiquidityCapSet` event.
        @param newLiquidityCap New liquidity cap value.
    */
    function setLiquidityCap(uint256 newLiquidityCap) external {
        _whenProtocolNotPaused();
        _isValidDelegateOrPoolAdmin();
        liquidityCap = newLiquidityCap;
        emit LiquidityCapSet(newLiquidityCap);
    }

    /**
        @dev   Sets the lockup period. Only the Pool Delegate can call this function.
        @dev   It emits a `LockupPeriodSet` event.
        @param newLockupPeriod New lockup period used to restrict the withdrawals.
    */
    function setLockupPeriod(uint256 newLockupPeriod) external {
        _isValidDelegateAndProtocolNotPaused();
        require(newLockupPeriod <= lockupPeriod, "P:BAD_VALUE");
        lockupPeriod = newLockupPeriod;
        emit LockupPeriodSet(newLockupPeriod);
    }

    /**
        @dev   Sets the staking fee. Only the Pool Delegate can call this function.
        @dev   It emits a `StakingFeeSet` event.
        @param newStakingFee New staking fee.
    */
    function setStakingFee(uint256 newStakingFee) external {
        _isValidDelegateAndProtocolNotPaused();
        require(newStakingFee.add(delegateFee) <= 10_000, "P:BAD_FEE");
        stakingFee = newStakingFee;
        emit StakingFeeSet(newStakingFee);
    }

    /**
        @dev   Sets the account status in the Pool's allowlist. Only the Pool Delegate can call this function.
        @dev   It emits an `LPStatusChanged` event.
        @param account The address to set status for.
        @param status  The status of an account in the allowlist.
    */
    function setAllowList(address account, bool status) external {
        _isValidDelegateAndProtocolNotPaused();
        allowedLiquidityProviders[account] = status;
        emit LPStatusChanged(account, status);
    }

    /**
        @dev   Sets a Pool Admin. Only the Pool Delegate can call this function.
        @dev   It emits a `PoolAdminSet` event.
        @param poolAdmin An address being allowed or disallowed as a Pool Admin.
        @param allowed Status of a Pool Admin.
    */
    function setPoolAdmin(address poolAdmin, bool allowed) external {
        _isValidDelegateAndProtocolNotPaused();
        poolAdmins[poolAdmin] = allowed;
        emit PoolAdminSet(poolAdmin, allowed);
    }

    /**
        @dev   Sets whether the Pool is open to the public. Only the Pool Delegate can call this function.
        @dev   It emits a `PoolOpenedToPublic` event.
        @param open Public pool access status.
    */
    function setOpenToPublic(bool open) external {
        _isValidDelegateAndProtocolNotPaused();
        openToPublic = open;
        emit PoolOpenedToPublic(open);
    }

    /************************************/
    /*** Liquidity Provider Functions ***/
    /************************************/

    /**
        @dev   Handles Liquidity Providers depositing of Liquidity Asset into the LiquidityLocker, minting PoolFDTs.
        @dev   It emits a `DepositDateUpdated` event.
        @dev   It emits a `BalanceUpdated` event.
        @dev   It emits a `Cooldown` event.
        @param amt Amount of Liquidity Asset to deposit.
    */
    function deposit(uint256 amt) external {
        _whenProtocolNotPaused();
        _isValidState(State.Finalized);
        require(isDepositAllowed(amt), "P:DEP_NOT_ALLOWED");

        withdrawCooldown[msg.sender] = uint256(0);  // Reset the LP's withdraw cooldown if they had previously intended to withdraw.

        uint256 wad = _toWad(amt);
        PoolLib.updateDepositDate(depositDate, balanceOf(msg.sender), wad, msg.sender);

        liquidityAsset.safeTransferFrom(msg.sender, liquidityLocker, amt);
        _mint(msg.sender, wad);

        _emitBalanceUpdatedEvent();
        emit Cooldown(msg.sender, uint256(0));
    }

    /**
        @dev Activates the cooldown period to withdraw. It can't be called if the account is not providing liquidity.
        @dev It emits a `Cooldown` event.
    **/
    function intendToWithdraw() external {
        require(balanceOf(msg.sender) != uint256(0), "P:ZERO_BAL");
        withdrawCooldown[msg.sender] = block.timestamp;
        emit Cooldown(msg.sender, block.timestamp);
    }

    /**
        @dev Cancels an initiated withdrawal by resetting the account's withdraw cooldown.
        @dev It emits a `Cooldown` event.
    **/
    function cancelWithdraw() external {
        require(withdrawCooldown[msg.sender] != uint256(0), "P:NOT_WITHDRAWING");
        withdrawCooldown[msg.sender] = uint256(0);
        emit Cooldown(msg.sender, uint256(0));
    }

    /**
        @dev   Checks that the account can withdraw an amount.
        @param account The address of the account.
        @param wad     The amount to withdraw.
    */
    function _canWithdraw(address account, uint256 wad) internal view {
        require(depositDate[account].add(lockupPeriod) <= block.timestamp,     "P:FUNDS_LOCKED");     // Restrict withdrawal during lockup period
        require(balanceOf(account).sub(wad) >= totalCustodyAllowance[account], "P:INSUF_TRANS_BAL");  // Account can only withdraw tokens that aren't custodied
    }

    /**
        @dev   Handles Liquidity Providers withdrawing of Liquidity Asset from the LiquidityLocker, burning PoolFDTs.
        @dev   It emits two `BalanceUpdated` event.
        @param amt Amount of Liquidity Asset to withdraw.
    */
    function withdraw(uint256 amt) external {
        _whenProtocolNotPaused();
        uint256 wad = _toWad(amt);
        (uint256 lpCooldownPeriod, uint256 lpWithdrawWindow) = _globals(superFactory).getLpCooldownParams();

        _canWithdraw(msg.sender, wad);
        require((block.timestamp - (withdrawCooldown[msg.sender] + lpCooldownPeriod)) <= lpWithdrawWindow, "P:WITHDRAW_NOT_ALLOWED");

        _burn(msg.sender, wad);  // Burn the corresponding PoolFDTs balance.
        withdrawFunds();         // Transfer full entitled interest, decrement `interestSum`.

        // Transfer amount that is due after realized losses are accounted for.
        // Recognized losses are absorbed by the LP.
        _transferLiquidityLockerFunds(msg.sender, amt.sub(_recognizeLosses()));

        _emitBalanceUpdatedEvent();
    }

    /**
        @dev   Transfers PoolFDTs.
        @param from Address sending   PoolFDTs.
        @param to   Address receiving PoolFDTs.
        @param wad  Amount of PoolFDTs to transfer.
    */
    function _transfer(address from, address to, uint256 wad) internal override {
        _whenProtocolNotPaused();

        (uint256 lpCooldownPeriod, uint256 lpWithdrawWindow) = _globals(superFactory).getLpCooldownParams();

        _canWithdraw(from, wad);
        require(block.timestamp > (withdrawCooldown[to] + lpCooldownPeriod + lpWithdrawWindow), "P:TO_NOT_ALLOWED");  // Recipient must not be currently withdrawing.
        require(recognizableLossesOf(from) == uint256(0),                                       "P:RECOG_LOSSES");    // If an LP has unrecognized losses, they must recognize losses using `withdraw`.

        PoolLib.updateDepositDate(depositDate, balanceOf(to), wad, to);
        super._transfer(from, to, wad);
    }

    /**
        @dev Withdraws all claimable interest from the LiquidityLocker for an account using `interestSum` accounting.
        @dev It emits a `BalanceUpdated` event.
    */
    function withdrawFunds() public override {
        _whenProtocolNotPaused();
        uint256 withdrawableFunds = _prepareWithdraw();

        if (withdrawableFunds == uint256(0)) return;

        _transferLiquidityLockerFunds(msg.sender, withdrawableFunds);
        _emitBalanceUpdatedEvent();

        interestSum = interestSum.sub(withdrawableFunds);

        _updateFundsTokenBalance();
    }

    /**
        @dev   Increases the custody allowance for a given Custodian corresponding to the calling account (`msg.sender`).
        @dev   It emits a `CustodyAllowanceChanged` event.
        @dev   It emits a `TotalCustodyAllowanceUpdated` event.
        @param custodian Address which will act as Custodian of a given amount for an account.
        @param amount    Number of additional FDTs to be custodied by the Custodian.
    */
    function increaseCustodyAllowance(address custodian, uint256 amount) external {
        uint256 oldAllowance      = custodyAllowance[msg.sender][custodian];
        uint256 newAllowance      = oldAllowance.add(amount);
        uint256 newTotalAllowance = totalCustodyAllowance[msg.sender].add(amount);

        PoolLib.increaseCustodyAllowanceChecks(custodian, amount, newTotalAllowance, balanceOf(msg.sender));

        custodyAllowance[msg.sender][custodian] = newAllowance;
        totalCustodyAllowance[msg.sender]       = newTotalAllowance;
        emit CustodyAllowanceChanged(msg.sender, custodian, oldAllowance, newAllowance);
        emit TotalCustodyAllowanceUpdated(msg.sender, newTotalAllowance);
    }

    /**
        @dev   Transfers custodied PoolFDTs back to the account.
        @dev   `from` and `to` should always be equal in this implementation.
        @dev   This means that the Custodian can only decrease their own allowance and unlock funds for the original owner.
        @dev   It emits a `CustodyTransfer` event.
        @dev   It emits a `CustodyAllowanceChanged` event.
        @dev   It emits a `TotalCustodyAllowanceUpdated` event.
        @param from   Address which holds the PoolFDTs.
        @param to     Address which will be the new owner of the amount of PoolFDTs.
        @param amount Amount of PoolFDTs transferred.
    */
    function transferByCustodian(address from, address to, uint256 amount) external {
        uint256 oldAllowance = custodyAllowance[from][msg.sender];
        uint256 newAllowance = oldAllowance.sub(amount);

        PoolLib.transferByCustodianChecks(from, to, amount);

        custodyAllowance[from][msg.sender] = newAllowance;
        uint256 newTotalAllowance          = totalCustodyAllowance[from].sub(amount);
        totalCustodyAllowance[from]        = newTotalAllowance;
        emit CustodyTransfer(msg.sender, from, to, amount);
        emit CustodyAllowanceChanged(from, msg.sender, oldAllowance, newAllowance);
        emit TotalCustodyAllowanceUpdated(msg.sender, newTotalAllowance);
    }

    /**************************/
    /*** 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 {
        PoolLib.reclaimERC20(token, address(liquidityAsset), _globals(superFactory));
    }

    /*************************/
    /*** Getter Functions ***/
    /*************************/

    /**
        @dev    Calculates the value of BPT in units of Liquidity Asset.
        @param  _bPool          Address of Balancer pool.
        @param  _liquidityAsset Asset used by Pool for liquidity to fund Loans.
        @param  _staker         Address that deposited BPTs to StakeLocker.
        @param  _stakeLocker    Escrows BPTs deposited by Staker.
        @return USDC value of staker BPTs.
    */
    function BPTVal(
        address _bPool,
        address _liquidityAsset,
        address _staker,
        address _stakeLocker
    ) external view returns (uint256) {
        return PoolLib.BPTVal(_bPool, _liquidityAsset, _staker, _stakeLocker);
    }

    /**
        @dev   Checks that the given deposit amount is acceptable based on current liquidityCap.
        @param depositAmt Amount of tokens (i.e liquidityAsset type) the account is trying to deposit.
    */
    function isDepositAllowed(uint256 depositAmt) public view returns (bool) {
        return (openToPublic || allowedLiquidityProviders[msg.sender]) &&
               _balanceOfLiquidityLocker().add(principalOut).add(depositAmt) <= liquidityCap;
    }

    /**
        @dev    Returns information on the stake requirements.
        @return [0] = Min amount of Liquidity Asset coverage from staking required.
                [1] = Present amount of Liquidity Asset coverage from the Pool Delegate stake.
                [2] = If enough stake is present from the Pool Delegate for finalization.
                [3] = Staked BPTs required for minimum Liquidity Asset coverage.
                [4] = Current staked BPTs.
    */
    function getInitialStakeRequirements() public view returns (uint256, uint256, bool, uint256, uint256) {
        return PoolLib.getInitialStakeRequirements(_globals(superFactory), stakeAsset, address(liquidityAsset), poolDelegate, stakeLocker);
    }

    /**
        @dev    Calculates BPTs required if burning BPTs for the Liquidity Asset, given supplied `tokenAmountOutRequired`.
        @param  _bPool                        The Balancer pool that issues the BPTs.
        @param  _liquidityAsset               Swap out asset (e.g. USDC) to receive when burning BPTs.
        @param  _staker                       Address that deposited BPTs to StakeLocker.
        @param  _stakeLocker                  Escrows BPTs deposited by Staker.
        @param  _liquidityAssetAmountRequired Amount of Liquidity Asset required to recover.
        @return [0] = poolAmountIn required.
                [1] = poolAmountIn currently staked.
    */
    function getPoolSharesRequired(
        address _bPool,
        address _liquidityAsset,
        address _staker,
        address _stakeLocker,
        uint256 _liquidityAssetAmountRequired
    ) external view returns (uint256, uint256) {
        return PoolLib.getPoolSharesRequired(_bPool, _liquidityAsset, _staker, _stakeLocker, _liquidityAssetAmountRequired);
    }

    /**
      @dev    Checks that the Pool state is `Finalized`.
      @return bool Boolean value indicating if Pool is in a Finalized state.
    */
    function isPoolFinalized() external view returns (bool) {
        return poolState == State.Finalized;
    }

    /************************/
    /*** Helper Functions ***/
    /************************/

    /**
        @dev   Converts to WAD precision.
        @param amt Amount to convert.
    */
    function _toWad(uint256 amt) internal view returns (uint256) {
        return amt.mul(WAD).div(10 ** liquidityAssetDecimals);
    }

    /**
        @dev    Returns the balance of this Pool's LiquidityLocker.
        @return Balance of LiquidityLocker.
    */
    function _balanceOfLiquidityLocker() internal view returns (uint256) {
        return liquidityAsset.balanceOf(liquidityLocker);
    }

    /**
        @dev   Checks that the current state of Pool matches the provided state.
        @param _state Enum of desired Pool state.
    */
    function _isValidState(State _state) internal view {
        require(poolState == _state, "P:BAD_STATE");
    }

    /**
        @dev Checks that `msg.sender` is the Pool Delegate.
    */
    function _isValidDelegate() internal view {
        require(msg.sender == poolDelegate, "P:NOT_DEL");
    }

    /**
        @dev Returns the MapleGlobals instance.
    */
    function _globals(address poolFactory) internal view returns (IMapleGlobals) {
        return IMapleGlobals(IPoolFactory(poolFactory).globals());
    }

    /**
        @dev Emits a `BalanceUpdated` event for LiquidityLocker.
        @dev It emits a `BalanceUpdated` event.
    */
    function _emitBalanceUpdatedEvent() internal {
        emit BalanceUpdated(liquidityLocker, address(liquidityAsset), _balanceOfLiquidityLocker());
    }

    /**
        @dev   Transfers Liquidity Asset to given `to` address, from self (i.e. `address(this)`).
        @param to    Address to transfer liquidityAsset.
        @param value Amount of liquidity asset that gets transferred.
    */
    function _transferLiquidityAsset(address to, uint256 value) internal {
        liquidityAsset.safeTransfer(to, value);
    }

    /**
        @dev Checks that `msg.sender` is the Pool Delegate or a Pool Admin.
    */
    function _isValidDelegateOrPoolAdmin() internal view {
        require(msg.sender == poolDelegate || poolAdmins[msg.sender], "P:NOT_DEL_OR_ADMIN");
    }

    /**
        @dev Checks that the protocol is not in a paused state.
    */
    function _whenProtocolNotPaused() internal view {
        require(!_globals(superFactory).protocolPaused(), "P:PROTO_PAUSED");
    }

    /**
        @dev Checks that `msg.sender` is the Pool Delegate and that the protocol is not in a paused state.
    */
    function _isValidDelegateAndProtocolNotPaused() internal view {
        _isValidDelegate();
        _whenProtocolNotPaused();
    }

    function _transferLiquidityLockerFunds(address to, uint256 value) internal {
        ILiquidityLocker(liquidityLocker).transfer(to, value);
    }

}

////// 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/PoolFactory.sol
/* pragma solidity 0.6.11; */

/* import "lib/openzeppelin-contracts/contracts/utils/Pausable.sol"; */

/* import "./Pool.sol"; */

/// @title PoolFactory instantiates Pools.
contract PoolFactory is Pausable {

    uint8 public constant LL_FACTORY = 3;  // Factory type of `LiquidityLockerFactory`.
    uint8 public constant SL_FACTORY = 4;  // Factory type of `StakeLockerFactory`.

    uint256  public poolsCreated;  // Incrementor for number of Pools created.
    IMapleGlobals public globals;  // A MapleGlobals instance.

    mapping(uint256 => address) public pools;              // Map to reference Pools corresponding to their respective indices.
    mapping(address => bool)    public isPool;             // True only if a Pool was instantiated by this factory.
    mapping(address => bool)    public poolFactoryAdmins;  // The PoolFactory Admin addresses that have permission to do certain operations in case of disaster management.

    event PoolFactoryAdminSet(address indexed poolFactoryAdmin, bool allowed);

    event PoolCreated(
        address indexed pool,
        address indexed delegate,
        address liquidityAsset,
        address stakeAsset,
        address liquidityLocker,
        address stakeLocker,
        uint256 stakingFee,
        uint256 delegateFee,
        uint256 liquidityCap,
        string  name,
        string  symbol
    );

    constructor(address _globals) public {
        globals = IMapleGlobals(_globals);
    }

    /**
        @dev   Sets MapleGlobals instance. Only the Governor can call this function.
        @param newGlobals Address of new MapleGlobals.
    */
    function setGlobals(address newGlobals) external {
        _isValidGovernor();
        globals = IMapleGlobals(newGlobals);
    }

    /**
        @dev    Instantiates a Pool.
        @dev    It emits a `PoolCreated` event.
        @param  liquidityAsset The asset escrowed in a LiquidityLocker.
        @param  stakeAsset     The asset escrowed in a StakeLocker.
        @param  slFactory      The factory to instantiate a StakeLocker from.
        @param  llFactory      The factory to instantiate a LiquidityLocker from.
        @param  stakingFee     Fee that Stakers earn on interest, in basis points.
        @param  delegateFee    Fee that the Pool Delegate earns on interest, in basis points.
        @param  liquidityCap   Amount of Liquidity Asset accepted by the Pool.
        @return poolAddress    Address of the instantiated Pool.
    */
    function createPool(
        address liquidityAsset,
        address stakeAsset,
        address slFactory,
        address llFactory,
        uint256 stakingFee,
        uint256 delegateFee,
        uint256 liquidityCap
    ) external whenNotPaused returns (address poolAddress) {
        _whenProtocolNotPaused();
        {
            IMapleGlobals _globals = globals;
            require(_globals.isValidSubFactory(address(this), llFactory, LL_FACTORY), "PF:INVALID_LLF");
            require(_globals.isValidSubFactory(address(this), slFactory, SL_FACTORY), "PF:INVALID_SLF");
            require(_globals.isValidPoolDelegate(msg.sender),                         "PF:NOT_DELEGATE");
        }

        string memory name   = "Maple Pool Token";
        string memory symbol = "MPL-LP";

        Pool pool =
            new Pool(
                msg.sender,
                liquidityAsset,
                stakeAsset,
                slFactory,
                llFactory,
                stakingFee,
                delegateFee,
                liquidityCap,
                name,
                symbol
            );

        poolAddress         = address(pool);
        pools[poolsCreated] = poolAddress;
        isPool[poolAddress] = true;
        ++poolsCreated;

        emit PoolCreated(
            poolAddress,
            msg.sender,
            liquidityAsset,
            stakeAsset,
            pool.liquidityLocker(),
            pool.stakeLocker(),
            stakingFee,
            delegateFee,
            liquidityCap,
            name,
            symbol
        );
    }

    /**
        @dev   Sets a PoolFactory Admin. Only the Governor can call this function.
        @dev   It emits a `PoolFactoryAdminSet` event.
        @param poolFactoryAdmin An address being allowed or disallowed as a PoolFactory Admin.
        @param allowed  Status of a PoolFactory Admin.
    */
    function setPoolFactoryAdmin(address poolFactoryAdmin, bool allowed) external {
        _isValidGovernor();
        poolFactoryAdmins[poolFactoryAdmin] = allowed;
        emit PoolFactoryAdminSet(poolFactoryAdmin, allowed);
    }

    /**
        @dev Triggers paused state. Halts functionality for certain functions. Only the Governor or a PoolFactory Admin can call this function.
    */
    function pause() external {
        _isValidGovernorOrPoolFactoryAdmin();
        super._pause();
    }

    /**
        @dev Triggers unpaused state. Restores functionality for certain functions. Only the Governor or a PoolFactory Admin can call this function.
    */
    function unpause() external {
        _isValidGovernorOrPoolFactoryAdmin();
        super._unpause();
    }

    /**
        @dev Checks that `msg.sender` is the Governor.
    */
    function _isValidGovernor() internal view {
        require(msg.sender == globals.governor(), "PF:NOT_GOV");
    }

    /**
        @dev Checks that `msg.sender` is the Governor or a PoolFactory Admin.
    */
    function _isValidGovernorOrPoolFactoryAdmin() internal view {
        require(msg.sender == globals.governor() || poolFactoryAdmins[msg.sender], "PF:NOT_GOV_OR_ADMIN");
    }

    /**
        @dev Checks that the protocol is not in a paused state.
    */
    function _whenProtocolNotPaused() internal view {
        require(!globals.protocolPaused(), "PF:PROTO_PAUSED");
    }

}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_globals","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"address","name":"liquidityAsset","type":"address"},{"indexed":false,"internalType":"address","name":"stakeAsset","type":"address"},{"indexed":false,"internalType":"address","name":"liquidityLocker","type":"address"},{"indexed":false,"internalType":"address","name":"stakeLocker","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakingFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegateFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidityCap","type":"uint256"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"}],"name":"PoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"poolFactoryAdmin","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"PoolFactoryAdminSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"LL_FACTORY","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SL_FACTORY","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"liquidityAsset","type":"address"},{"internalType":"address","name":"stakeAsset","type":"address"},{"internalType":"address","name":"slFactory","type":"address"},{"internalType":"address","name":"llFactory","type":"address"},{"internalType":"uint256","name":"stakingFee","type":"uint256"},{"internalType":"uint256","name":"delegateFee","type":"uint256"},{"internalType":"uint256","name":"liquidityCap","type":"uint256"}],"name":"createPool","outputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"globals","outputs":[{"internalType":"contract IMapleGlobals","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":[{"internalType":"address","name":"","type":"address"}],"name":"poolFactoryAdmins","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolsCreated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newGlobals","type":"address"}],"name":"setGlobals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolFactoryAdmin","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setPoolFactoryAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50604051615fe3380380615fe38339818101604052602081101561003357600080fd5b50516000805460ff19169055600280546001600160a01b039092166001600160a01b0319909216919091179055615f748061006f6000396000f3fe60806040523480156200001157600080fd5b5060043610620000e25760003560e01c8063945164e71162000099578063c3124525116200006f578063c3124525146200023a578063c5ba73ed1462000244578063cc2e0a26146200024e578063f3f616c0146200027757620000e2565b8063945164e714620001de5780639f71f14a14620001fa578063ac4afa38146200021a57620000e2565b8063312b898214620000e75780633f4ba83a14620001585780635b16ebb714620001645780635c975abb14620001a15780636050abb214620001ab5780638456cb5914620001d4575b600080fd5b6200013c600480360360e0811015620000ff57600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359091169060808101359060a08101359060c00135620002a8565b604080516001600160a01b039092168252519081900360200190f35b6200016262000a23565b005b6200018d600480360360208110156200017c57600080fd5b50356001600160a01b031662000a39565b604080519115158252519081900360200190f35b6200018d62000a4e565b6200018d60048036036020811015620001c357600080fd5b50356001600160a01b031662000a57565b6200016262000a6c565b620001e862000a80565b60408051918252519081900360200190f35b6200020462000a86565b6040805160ff9092168252519081900360200190f35b6200013c600480360360208110156200023257600080fd5b503562000a8b565b6200013c62000aa6565b6200020462000ab5565b62000162600480360360208110156200026657600080fd5b50356001600160a01b031662000aba565b62000162600480360360408110156200028f57600080fd5b506001600160a01b038135169060200135151562000ae6565b6000805460ff1615620002f5576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b620002ff62000b50565b600254604080516313d459fb60e01b81523060048201526001600160a01b038881166024830152600360448301529151919092169182916313d459fb91606480820192602092909190829003018186803b1580156200035d57600080fd5b505afa15801562000372573d6000803e3d6000fd5b505050506040513d60208110156200038957600080fd5b5051620003ce576040805162461bcd60e51b815260206004820152600e60248201526d28231d24a72b20a624a22fa6262360911b604482015290519081900360640190fd5b604080516313d459fb60e01b8152306004828101919091526001600160a01b038a8116602484015260448301919091529151918316916313d459fb91606480820192602092909190829003018186803b1580156200042b57600080fd5b505afa15801562000440573d6000803e3d6000fd5b505050506040513d60208110156200045757600080fd5b50516200049c576040805162461bcd60e51b815260206004820152600e60248201526d28231d24a72b20a624a22fa9a62360911b604482015290519081900360640190fd5b60408051631b971aa360e11b815233600482015290516001600160a01b0383169163372e3546916024808301926020929190829003018186803b158015620004e357600080fd5b505afa158015620004f8573d6000803e3d6000fd5b505050506040513d60208110156200050f57600080fd5b505162000555576040805162461bcd60e51b815260206004820152600f60248201526e50463a4e4f545f44454c454741544560881b604482015290519081900360640190fd5b5060606040518060400160405280601081526020016f26b0b83632902837b7b6102a37b5b2b760811b815250905060606040518060400160405280600681526020016504d504c2d4c560d41b81525090506000338b8b8b8b8b8b8b8a8a604051620005c09062000eed565b6001600160a01b03808c1682528a81166020808401919091528a821660408401528982166060840152908816608083015260a0820187905260c0820186905260e0820185905261014061010083018181528551918401919091528451909161012084019161016085019187019080838360005b838110156200064d57818101518382015260200162000633565b50505050905090810190601f1680156200067b5780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015620006b057818101518382015260200162000696565b50505050905090810190601f168015620006de5780820380516001836020036101000a031916815260200191505b509c50505050505050505050505050604051809103906000f0801580156200070a573d6000803e3d6000fd5b5090508093508360036000600154815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600160046000866001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff02191690831515021790555060016000815460010191905081905550336001600160a01b0316846001600160a01b03167fca6c57d58de1ff84c629ea4a44b3a2f19a6c83cc1dc3cd7d1ede2067a6a1016d8d8d856001600160a01b0316639759164a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200080957600080fd5b505afa1580156200081e573d6000803e3d6000fd5b505050506040513d60208110156200083557600080fd5b5051604080516233b1cf60e41b815290516001600160a01b0389169163033b1cf0916004808301926020929190829003018186803b1580156200087757600080fd5b505afa1580156200088c573d6000803e3d6000fd5b505050506040513d6020811015620008a357600080fd5b81019080805190602001909291905050508d8d8d8c8c604051808a6001600160a01b03166001600160a01b03168152602001896001600160a01b03166001600160a01b03168152602001886001600160a01b03166001600160a01b03168152602001876001600160a01b03166001600160a01b031681526020018681526020018581526020018481526020018060200180602001838103835285818151815260200191508051906020019080838360005b838110156200096e57818101518382015260200162000954565b50505050905090810190601f1680156200099c5780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015620009d1578181015183820152602001620009b7565b50505050905090810190601f168015620009ff5780820380516001836020036101000a031916815260200191505b509b50505050505050505050505060405180910390a3505050979650505050505050565b62000a2d62000c12565b62000a3762000cfd565b565b60046020526000908152604090205460ff1681565b60005460ff1690565b60056020526000908152604090205460ff1681565b62000a7662000c12565b62000a3762000d9e565b60015481565b600481565b6003602052600090815260409020546001600160a01b031681565b6002546001600160a01b031681565b600381565b62000ac462000e22565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b62000af062000e22565b6001600160a01b038216600081815260056020908152604091829020805460ff1916851515908117909155825190815291517fc0d8ae32722eafe08b35b2bb46dec31dcbbd5bb18069f66995df3c9f47f4d64f9281900390910190a25050565b600260009054906101000a90046001600160a01b03166001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801562000b9f57600080fd5b505afa15801562000bb4573d6000803e3d6000fd5b505050506040513d602081101562000bcb57600080fd5b50511562000a37576040805162461bcd60e51b815260206004820152600f60248201526e14118e941493d513d7d4105554d151608a1b604482015290519081900360640190fd5b600260009054906101000a90046001600160a01b03166001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b15801562000c6157600080fd5b505afa15801562000c76573d6000803e3d6000fd5b505050506040513d602081101562000c8d57600080fd5b50516001600160a01b031633148062000cb557503360009081526005602052604090205460ff165b62000a37576040805162461bcd60e51b815260206004820152601360248201527228231d2727aa2fa3a7ab2fa7a92fa0a226a4a760691b604482015290519081900360640190fd5b60005460ff1662000d4c576040805162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015290519081900360640190fd5b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa62000d8162000ee9565b604080516001600160a01b039092168252519081900360200190a1565b60005460ff161562000dea576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25862000d8162000ee9565b600260009054906101000a90046001600160a01b03166001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b15801562000e7157600080fd5b505afa15801562000e86573d6000803e3d6000fd5b505050506040513d602081101562000e9d57600080fd5b50516001600160a01b0316331462000a37576040805162461bcd60e51b815260206004820152600a60248201526928231d2727aa2fa3a7ab60b11b604482015290519081900360640190fd5b3390565b6150438062000efc8339019056fe6101806040523480156200001257600080fd5b50604051620050433803806200504383398181016040526101408110156200003957600080fd5b815160208301516040808501516060860151608087015160a088015160c089015160e08a01516101008b0180519751999b989a969995989497939692959194919392820192846401000000008211156200009257600080fd5b908301906020820185811115620000a857600080fd5b8251640100000000811182820188101715620000c357600080fd5b82525081516020918201929091019080838360005b83811015620000f2578181015183820152602001620000d8565b50505050905090810190601f168015620001205780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200014457600080fd5b9083019060208201858111156200015a57600080fd5b82516401000000008111828201881017156200017557600080fd5b82525081516020918201929091019080838360005b83811015620001a45781810151838201526020016200018a565b50505050905090810190601f168015620001d25780820380516001836020036101000a031916815260200191505b5060405250505081818181818181818160039080519060200190620001f99291906200056e565b5080516200020f9060049060208401906200056e565b50506005805460ff1916601217905550732c1c30fb8cc313ef3cfd2e2bbf2da88add902c30955063149851689450620002589350339250506001600160e01b03620004fe169050565b604080516001600160e01b031960e085901b1681526001600160a01b039283166004820152828e166024820152918c16604483015260648201899052608482018890525160a4808301926000929190829003018186803b158015620002bc57600080fd5b505af4158015620002d1573d6000803e3d6000fd5b50505050886001600160a01b03166080816001600160a01b031660601b81525050886001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156200032c57600080fd5b505afa15801562000341573d6000803e3d6000fd5b505050506040513d60208110156200035857600080fd5b505160ff1661014052606088811b6001600160601b031990811660e0528b821b1660a052601086905561016085905233901b610120526012839055604080516342ef033f60e11b81526001600160a01b03808b1660048301528b811660248301529151918916916385de067e916044808201926020929091908290030181600087803b158015620003e857600080fd5b505af1158015620003fd573d6000803e3d6000fd5b505050506040513d60208110156200041457600080fd5b505160601b6001600160601b0319166101005260408051630cf5bc1d60e11b81526001600160a01b038b811660048301529151918816916319eb783a916024808201926020929091908290030181600087803b1580156200047457600080fd5b505af115801562000489573d6000803e3d6000fd5b505050506040513d6020811015620004a057600080fd5b505160601b6001600160601b03191660c05262ed4e00601355604080516000815290517f24b0afb747a8213aea796b9518bfa667de187b83390eda7cc93b8e57f80fcd1a916020908290030190a15050505050505050505062000613565b6000816001600160a01b031663c31245256040518163ffffffff1660e01b815260040160206040518083038186803b1580156200053a57600080fd5b505afa1580156200054f573d6000803e3d6000fd5b505050506040513d60208110156200056657600080fd5b505192915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620005b157805160ff1916838001178555620005e1565b82800160010185558215620005e1579182015b82811115620005e1578251825591602001919060010190620005c4565b50620005ef929150620005f3565b5090565b6200061091905b80821115620005ef5760008155600101620005fa565b90565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c61014051610160516148fd62000746600039806111395280611c695280612644525080613397525080610d5f5280610da85280610f5f52806119ae5280611ed152806123b15280612c755280613020525080610c875280610e6b528061127752806112f2528061139f52806114195280612e45525080610df3528061225f5280612e6d525080610f8752806112a1528061251052806127285280612c0c5280612f7552806132ac5280613805525080610e43528061124d5280611b4c5280612d4b5280613b28525080610e1b5280611027528061137552806113ea5280611efa528061238d52806127055280612be25280612dd95280612e1d5280612f5352806137d652506148fd6000f3fe608060405234801561001057600080fd5b50600436106103fb5760003560e01c806370a0823111610215578063a9059cbb11610125578063c771c390116100b8578063d82745c811610087578063d82745c814610bfa578063dd62ed3e14610c20578063ee947a7c14610c4e578063eff9884314610c56578063fec984e314610c5e576103fb565b8063c771c39014610b79578063c965b54814610b96578063cc0fef0214610bc4578063d7bd3c9114610bcc576103fb565b8063b69410de116100f4578063b69410de14610acf578063b6b55f2514610ad7578063c374682514610af4578063c59e395914610b53576103fb565b8063a9059cbb14610a4f578063ac64165514610a7b578063aed4966a14610a83578063af6d557114610aa9576103fb565b806384b76824116101a85780639759164a116101775780639759164a146109ec5780639f3c7325146109f4578063a33142f7146109fc578063a43baa3d14610a04578063a457c2d714610a23576103fb565b806384b76824146109885780638905fd4f146109905780639185192a146109b657806395d89b41146109e4576103fb565b806376687d3d116101e457806376687d3d1461093e5780637b99adb11461094657806380cd916d1461096357806380e7ce851461096b576103fb565b806370a08231146108da57806371073bac1461090057806373ef9a50146109085780637666f12514610910576103fb565b80632e1a7d4d1161031057806346c162de116102a357806351b42b001161027257806351b42b001461081c578063613384f214610824578063641ad8a91461084a5780636696779114610876578063681cb10a1461089c576103fb565b806346c162de146107de5780634bb278f3146107e65780634e97415f146107ee5780634f85221a14610814576103fb565b806340504ba0116102df57806340504ba01461074757806340bde09814610775578063410dbf7e1461079b578063443bb293146107b8576103fb565b80632e1a7d4d146106ee578063313ce5671461070b57806339509351146107135780634046af2b1461073f576103fb565b80631831ccf21161039357806323b872dd1161036257806323b872dd1461062857806324600fc31461065e57806324b92e8e1461066657806327f918561461068c5780632ac04ac8146106b8576103fb565b80631831ccf21461057a5780631aa37cec14610582578063209b2bca146105ba57806321c0b342146105c2576103fb565b80630d49b38c116103cf5780630d49b38c1461051957806313bf9e7e14610521578063174a5be41461055457806318160ddd14610572576103fb565b806241c52c14610400578063033b1cf01461043857806306fdde031461045c578063095ea7b3146104d9575b600080fd5b6104266004803603602081101561041657600080fd5b50356001600160a01b0316610c66565b60408051918252519081900360200190f35b610440610c85565b604080516001600160a01b039092168252519081900360200190f35b610464610ca9565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561049e578181015183820152602001610486565b50505050905090810190601f1680156104cb5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610505600480360360408110156104ef57600080fd5b506001600160a01b038135169060200135610d3f565b604080519115158252519081900360200190f35b610440610d5d565b610529610d81565b6040805195865260208601949094529115158484015260608401526080830152519081900360a00190f35b61055c610f04565b6040805160ff9092168252519081900360200190f35b610426610f09565b610505610f0f565b6105b86004803603606081101561059857600080fd5b506001600160a01b03813581169160208101359091169060400135610f18565b005b610440611025565b6105f0600480360360408110156105d857600080fd5b506001600160a01b0381358116916020013516611049565b604051808260e080838360005b838110156106155781810151838201526020016105fd565b5050505090500191505060405180910390f35b6105056004803603606081101561063e57600080fd5b506001600160a01b03813581169160208101359091169060400135611525565b6105b86115b3565b6104266004803603602081101561067c57600080fd5b50356001600160a01b0316611607565b6105b8600480360360408110156106a257600080fd5b506001600160a01b038135169060200135611619565b6105b8600480360360608110156106ce57600080fd5b506001600160a01b038135811691602081013590911690604001356117ba565b6105b86004803603602081101561070457600080fd5b5035611991565b61055c611aed565b6105056004803603604081101561072957600080fd5b506001600160a01b038135169060200135611af6565b610440611b4a565b6105b86004803603604081101561075d57600080fd5b506001600160a01b0381358116916020013516611b6e565b6104266004803603602081101561078b57600080fd5b50356001600160a01b0316611bef565b6105b8600480360360208110156107b157600080fd5b5035611c58565b610426600480360360208110156107ce57600080fd5b50356001600160a01b0316611d0d565b6105b8611d3f565b6105b8611d6d565b6104266004803603602081101561080457600080fd5b50356001600160a01b0316611e3b565b610505611e80565b6105b8611ea0565b6105056004803603602081101561083a57600080fd5b50356001600160a01b0316611ff9565b61085261200e565b6040518082600281111561086257fe5b60ff16815260200191505060405180910390f35b6104266004803603602081101561088c57600080fd5b50356001600160a01b031661201c565b610426600480360360808110156108b257600080fd5b506001600160a01b038135811691602081013582169160408201358116916060013516612042565b610426600480360360208110156108f057600080fd5b50356001600160a01b03166120ed565b610426612108565b6105b861210e565b6105b86004803603604081101561092657600080fd5b506001600160a01b03813516906020013515156121a4565b61042661220c565b6105b86004803603602081101561095c57600080fd5b5035612212565b61044061225d565b6105056004803603602081101561098157600080fd5b5035612281565b6105b86122d4565b6105b8600480360360208110156109a657600080fd5b50356001600160a01b0316612370565b6105b8600480360360408110156109cc57600080fd5b506001600160a01b0381351690602001351515612445565b6104646124ad565b61044061250e565b610426612532565b610426612538565b6105b860048036036020811015610a1a57600080fd5b5035151561253e565b61050560048036036040811015610a3957600080fd5b506001600160a01b03813516906020013561258d565b61050560048036036040811015610a6557600080fd5b506001600160a01b0381351690602001356125fb565b61042661260f565b61042660048036036020811015610a9957600080fd5b50356001600160a01b0316612615565b61042660048036036020811015610abf57600080fd5b50356001600160a01b0316612630565b610426612642565b6105b860048036036020811015610aed57600080fd5b5035612666565b610b3a600480360360a0811015610b0a57600080fd5b506001600160a01b03813581169160208101358216916040820135811691606081013590911690608001356127a0565b6040805192835260208301919091528051918290030190f35b61050560048036036020811015610b6957600080fd5b50356001600160a01b031661285d565b6105b860048036036020811015610b8f57600080fd5b5035612872565b61042660048036036040811015610bac57600080fd5b506001600160a01b03813581169160200135166128fa565b6105b8612917565b61044060048036036040811015610be257600080fd5b506001600160a01b0381358116916020013516612942565b61042660048036036020811015610c1057600080fd5b50356001600160a01b0316612968565b61042660048036036040811015610c3657600080fd5b506001600160a01b038135811691602001351661297a565b6104266129a5565b6104266129ab565b6104266129b1565b6001600160a01b0381166000908152600860205260409020545b919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60038054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610d355780601f10610d0a57610100808354040283529160200191610d35565b820191906000526020600020905b815481529060010190602001808311610d1857829003601f168201915b5050505050905090565b6000610d53610d4c6129b7565b84846129bb565b5060015b92915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806000806000732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3063767f5038610dcc7f0000000000000000000000000000000000000000000000000000000000000000612aa7565b6040805160e084901b6001600160e01b03191681526001600160a01b0392831660048201527f0000000000000000000000000000000000000000000000000000000000000000831660248201527f0000000000000000000000000000000000000000000000000000000000000000831660448201527f0000000000000000000000000000000000000000000000000000000000000000831660648201527f000000000000000000000000000000000000000000000000000000000000000090921660848301525160a48083019260a0929190829003018186803b158015610eb257600080fd5b505af4158015610ec6573d6000803e3d6000fd5b505050506040513d60a0811015610edc57600080fd5b5080516020820151604083015160608401516080909401519299919850965091945092509050565b600181565b60025490565b60145460ff1681565b610f20612b14565b610f2a6001612b24565b601154610f3d908263ffffffff612b8616565b6011556040805163fbecb17160e01b8152601660048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301527f00000000000000000000000000000000000000000000000000000000000000008116604483015280861660648301528416608482015260a481018390529051732c1c30fb8cc313ef3cfd2e2bbf2da88add902c309163fbecb1719160c4808301926000929190829003018186803b15801561100057600080fd5b505af4158015611014573d6000803e3d6000fd5b50505050611020612be0565b505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6110516146af565b611059612c70565b611061612d40565b6001600160a01b0380841660009081526016602090815260408083208685168452909152808220548151634e71d92d60e01b81529151931692634e71d92d9260048084019360e093929083900390910190829087803b1580156110c357600080fd5b505af11580156110d7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525060e08110156110fc57600080fd5b50601054604051633faa6c5d60e01b8152919250600091829182918291732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3091633faa6c5d9188917f00000000000000000000000000000000000000000000000000000000000000009190600401808460e08083838c5b8381101561117e578181015183820152602001611166565b50505050905001838152602001828152602001935050505060806040518083038186803b1580156111ae57600080fd5b505af41580156111c2573d6000803e3d6000fd5b505050506040513d60808110156111d857600080fd5b50805160208201516040830151606090930151601154929750909550919350909150821161120e57601180548390039055611232565b601154611224908290840363ffffffff612b8616565b601180546000909155925090505b600c54611245908263ffffffff612b8616565b600c556112727f000000000000000000000000000000000000000000000000000000000000000085612dcc565b61129c7f000000000000000000000000000000000000000000000000000000000000000084612dcc565b6112d57f00000000000000000000000000000000000000000000000000000000000000006112d0848463ffffffff612b8616565b612dcc565b60c0850151156112f0576112f0878660066020020151612e06565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166346c162de6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561134b57600080fd5b505af115801561135f573d6000803e3d6000fd5b5050505061136b611d3f565b611373612be0565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f2047d1633ff7768462ae07d28cb16e484203bfd6d85ce832494270ebcd9081a27f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a082317f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561147e57600080fd5b505afa158015611492573d6000803e3d6000fd5b505050506040513d60208110156114a857600080fd5b505160408051918252519081900360200190a36060808601516040805184815260208101869052808201929092529181018590526080810186905290516001600160a01b038916917f21280d282ce6aa29c649fd1825373d7c77892fac3f1958fd98d5ca52dd82a197919081900360a00190a25050505092915050565b6000611532848484613010565b6115a88461153e6129b7565b6115a3856040518060600160405280602881526020016147c3602891396001600160a01b038a1660009081526001602052604081209061157c6129b7565b6001600160a01b03168152602081019190915260400160002054919063ffffffff61318e16565b6129bb565b5060015b9392505050565b6115bb612c70565b60006115c5613225565b9050806115d25750611605565b6115dc33826132aa565b6115e4612be0565b600c546115f7908263ffffffff61332a16565b600c5561160261336c565b50505b565b60156020526000908152604090205481565b336000908152601a602090815260408083206001600160a01b03861684529091528120549061164e828463ffffffff612b8616565b336000908152601b602052604081205491925090611672908563ffffffff612b8616565b9050732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3063297e7bf786868461169a336120ed565b6040518563ffffffff1660e01b815260040180856001600160a01b03166001600160a01b0316815260200184815260200183815260200182815260200194505050505060006040518083038186803b1580156116f557600080fd5b505af4158015611709573d6000803e3d6000fd5b5050336000818152601a602090815260408083206001600160a01b038c16808552908352818420899055848452601b835292819020879055805189815291820188905280519295509293507f847e03d69a7075471d42285f4ac63570c10f3012d8bf736d66de2eef17aac3e892908290030190a360408051828152905133917fe7f3fb4dacbff434e6d283d891f199c48b05b1629f610bd7ddc62353e162fb16919081900360200190a25050505050565b6001600160a01b0383166000908152601a60209081526040808320338452909152812054906117ef828463ffffffff61332a16565b60408051631b4c903160e01b81526001600160a01b03808916600483015287166024820152604481018690529051919250732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3091631b4c903191606480820192600092909190829003018186803b15801561185c57600080fd5b505af4158015611870573d6000803e3d6000fd5b505050506001600160a01b0385166000818152601a602090815260408083203384528252808320859055928252601b9052908120546118af908561332a565b6001600160a01b038088166000818152601b60209081526040918290208590558151898152915194955092891693919233927ffaa022ea2cd7f14157070896fabadafe96cc4d4714eef7ae6a992a5084493ed59281900390910190a46040805184815260208101849052815133926001600160a01b038a16927f847e03d69a7075471d42285f4ac63570c10f3012d8bf736d66de2eef17aac3e8929081900390910190a360408051828152905133917fe7f3fb4dacbff434e6d283d891f199c48b05b1629f610bd7ddc62353e162fb16919081900360200190a2505050505050565b611999612c70565b60006119a482613390565b90506000806119d27f0000000000000000000000000000000000000000000000000000000000000000612aa7565b6001600160a01b0316639f51290b6040518163ffffffff1660e01b8152600401604080518083038186803b158015611a0957600080fd5b505afa158015611a1d573d6000803e3d6000fd5b505050506040513d6040811015611a3357600080fd5b5080516020909101519092509050611a4b33846133dd565b3360009081526019602052604090205482014203811015611aac576040805162461bcd60e51b8152602060048201526016602482015275140e95d2551211149055d7d393d517d0531313d5d15160521b604482015290519081900360640190fd5b611ab633846134bc565b611abe6115b3565b611adf33611ada611acd613563565b879063ffffffff61332a16565b6132aa565b611ae7612be0565b50505050565b60055460ff1690565b6000610d53611b036129b7565b846115a38560016000611b146129b7565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff612b8616565b7f000000000000000000000000000000000000000000000000000000000000000081565b611b76612b14565b6001600160a01b038083166000908152601660209081526040808320858516845290915280822054815163175f832960e01b8152915193169263175f83299260048084019391929182900301818387803b158015611bd357600080fd5b505af1158015611be7573d6000803e3d6000fd5b505050505050565b6001600160a01b0381166000908152600a6020526040812054600160801b90611c4a90611c4590611c39611c34611c25886120ed565b6009549063ffffffff61359216565b6135eb565b9063ffffffff61362c16565b613691565b81611c5157fe5b0492915050565b611c60612b14565b612710611c93827f000000000000000000000000000000000000000000000000000000000000000063ffffffff612b8616565b1115611cd2576040805162461bcd60e51b8152602060048201526009602482015268503a4241445f46454560b81b604482015290519081900360640190fd5b60108190556040805182815290517f9408bb8c08d29b335e36090045074610352365476d9df02e203c25db4fcd67c09181900360200190a150565b6001600160a01b038116600090815260086020526040812054610d5790611d3384611e3b565b9063ffffffff61332a16565b6000611d4961336c565b905060008113611d595750611605565b611d6a611d6582613691565b6136d2565b50565b611d75612b14565b611d7f6000612b24565b6000611d89610d81565b50509250505080611dd1576040805162461bcd60e51b815260206004820152600d60248201526c503a494e5355465f5354414b4560981b604482015290519081900360640190fd5b601480546001919061ff0019166101008302179055506014546040517f24b0afb747a8213aea796b9518bfa667de187b83390eda7cc93b8e57f80fcd1a91610100900460ff169080826002811115611e2557fe5b60ff16815260200191505060405180910390a150565b6001600160a01b038116600090815260076020526040812054600160801b90611c4a90611c4590611c39611c34611e71886120ed565b6006549063ffffffff61359216565b60006001601454610100900460ff166002811115611e9a57fe5b14905090565b611ea8612b14565b611eb26001612b24565b732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3063abbedb40611ef57f0000000000000000000000000000000000000000000000000000000000000000612aa7565b6011547f00000000000000000000000000000000000000000000000000000000000000006040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001838152602001826001600160a01b03166001600160a01b03168152602001935050505060006040518083038186803b158015611f7f57600080fd5b505af4158015611f93573d6000803e3d6000fd5b50506014805461ff00191661020017908190556040517f24b0afb747a8213aea796b9518bfa667de187b83390eda7cc93b8e57f80fcd1a935061010090910460ff16915080826002811115611fe457fe5b60ff16815260200191505060405180910390a1565b60176020526000908152604090205460ff1681565b601454610100900460ff1681565b6001600160a01b0381166000908152600b6020526040812054610d5790611d3384611bef565b6040805163340e588560e11b81526001600160a01b03808716600483015280861660248301528085166044830152831660648201529051600091732c1c30fb8cc313ef3cfd2e2bbf2da88add902c309163681cb10a91608480820192602092909190829003018186803b1580156120b857600080fd5b505af41580156120cc573d6000803e3d6000fd5b505050506040513d60208110156120e257600080fd5b505195945050505050565b6001600160a01b031660009081526020819052604090205490565b600c5481565b6000612119336120ed565b1415612159576040805162461bcd60e51b815260206004820152600a602482015269140e96915493d7d0905360b21b604482015290519081900360640190fd5b336000818152601960209081526040918290204290819055825190815291517f8a05f911d8ab7fc50fec37ef4ba7f9bfcb1a3c191c81dcd824ad0946c4e20d659281900390910190a2565b6121ac612b14565b6001600160a01b038216600081815260186020908152604091829020805460ff1916851515908117909155825190815291517fdf56132520665b33cd5731c5cfbacd8bee82524e67df563bb25b2be304f91d449281900390910190a25050565b60125481565b61221a612c70565b612222612d40565b60128190556040805182815290517f3ff20538222f568f27ff436c0c49dfd3e48d5b8f86533a3f759dc1c7089775ab9181900360200190a150565b7f000000000000000000000000000000000000000000000000000000000000000081565b60145460009060ff16806122a457503360009081526018602052604090205460ff165b8015610d5757506012546122cc836122c06011546122c06137d2565b9063ffffffff612b8616565b111592915050565b33600090815260196020526040902054612329576040805162461bcd60e51b8152602060048201526011602482015270503a4e4f545f5749544844524157494e4760781b604482015290519081900360640190fd5b3360008181526019602090815260408083208390558051928352517f8a05f911d8ab7fc50fec37ef4ba7f9bfcb1a3c191c81dcd824ad0946c4e20d659281900390910190a2565b732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3063a89d5ddb827f00000000000000000000000000000000000000000000000000000000000000006123d57f0000000000000000000000000000000000000000000000000000000000000000612aa7565b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015292841660248401529216604482015290516064808301926000929190829003018186803b15801561242a57600080fd5b505af415801561243e573d6000803e3d6000fd5b5050505050565b61244d612b14565b6001600160a01b038216600081815260176020908152604091829020805460ff1916851515908117909155825190815291517f353578bbc0ab907b7018b0f7b50b5f822d31dc9fcf4c16fffa780e109ca7c9309281900390910190a25050565b60048054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610d355780601f10610d0a57610100808354040283529160200191610d35565b7f000000000000000000000000000000000000000000000000000000000000000081565b600e5481565b600d5481565b612546612b14565b6014805482151560ff19909116811790915560408051918252517feeba6fd794e30165023f7e3d017e92901622076a95d36e45906955e025ff4fe79181900360200190a150565b6000610d5361259a6129b7565b846115a3856040518060600160405280602581526020016148a360259139600160006125c46129b7565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff61318e16565b6000610d536126086129b7565b8484613010565b60115481565b6001600160a01b03166000908152600b602052604090205490565b601b6020526000908152604090205481565b7f000000000000000000000000000000000000000000000000000000000000000081565b61266e612c70565b6126786001612b24565b61268181612281565b6126c6576040805162461bcd60e51b8152602060048201526011602482015270140e91115417d393d517d0531313d5d151607a1b604482015290519081900360640190fd5b3360009081526019602052604081208190556126e182613390565b90506126f860156126f1336120ed565b833361389b565b6127536001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016337f00000000000000000000000000000000000000000000000000000000000000008563ffffffff61395416565b61275d33826139ae565b612765612be0565b6040805160008152905133917f8a05f911d8ab7fc50fec37ef4ba7f9bfcb1a3c191c81dcd824ad0946c4e20d65919081900360200190a25050565b6040805163c374682560e01b81526001600160a01b03808816600483015280871660248301528086166044830152841660648201526084810183905281516000928392732c1c30fb8cc313ef3cfd2e2bbf2da88add902c309263c37468259260a480840193919291829003018186803b15801561281c57600080fd5b505af4158015612830573d6000803e3d6000fd5b505050506040513d604081101561284657600080fd5b508051602090910151909890975095505050505050565b60186020526000908152604090205460ff1681565b61287a612b14565b6013548111156128bf576040805162461bcd60e51b815260206004820152600b60248201526a503a4241445f56414c554560a81b604482015290519081900360640190fd5b60138190556040805182815290517f3094b4ce0463766c3cd81ed2ae2451610dcac39a1061fa023ca9d3d4df959f759181900360200190a150565b601a60209081526000928352604080842090915290825290205481565b60006129216139fa565b9050600081136129315750611605565b611d6a61293d82613691565b613a18565b60166020908152600092835260408084209091529082529020546001600160a01b031681565b60196020526000908152604090205481565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60135481565b60105481565b600f5481565b3390565b6001600160a01b038316612a005760405162461bcd60e51b81526004018080602001828103825260248152602001806148316024913960400191505060405180910390fd5b6001600160a01b038216612a455760405162461bcd60e51b81526004018080602001828103825260228152602001806147136022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6000816001600160a01b031663c31245256040518163ffffffff1660e01b815260040160206040518083038186803b158015612ae257600080fd5b505afa158015612af6573d6000803e3d6000fd5b505050506040513d6020811015612b0c57600080fd5b505192915050565b612b1c613b1d565b611605612c70565b806002811115612b3057fe5b601454610100900460ff166002811115612b4657fe5b14611d6a576040805162461bcd60e51b815260206004820152600b60248201526a503a4241445f535441544560a81b604482015290519081900360640190fd5b6000828201838110156115ac576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f2047d1633ff7768462ae07d28cb16e484203bfd6d85ce832494270ebcd9081a2612c5d6137d2565b60408051918252519081900360200190a3565b612c997f0000000000000000000000000000000000000000000000000000000000000000612aa7565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015612cd157600080fd5b505afa158015612ce5573d6000803e3d6000fd5b505050506040513d6020811015612cfb57600080fd5b505115611605576040805162461bcd60e51b815260206004820152600e60248201526d140e941493d513d7d4105554d15160921b604482015290519081900360640190fd5b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480612d8657503360009081526017602052604090205460ff165b611605576040805162461bcd60e51b8152602060048201526012602482015271281d2727aa2fa222a62fa7a92fa0a226a4a760711b604482015290519081900360640190fd5b6116026001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016838363ffffffff613b8616565b6040805162715b0960e41b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000811660248301527f000000000000000000000000000000000000000000000000000000000000000016604482015260648101839052905160009182918291732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3091630715b09091608480820192606092909190829003018186803b158015612ede57600080fd5b505af4158015612ef2573d6000803e3d6000fd5b505050506040513d6060811015612f0857600080fd5b5080516020820151604090920151909450909250905080841115612f4657600d54612f3b9082860363ffffffff612b8616565b600d55612f46612917565b612fa06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000008363ffffffff613b8616565b601154612fb3908563ffffffff61332a16565b60115560408051858152602081018590528082018490526060810183905290516001600160a01b038716917fd393d18014c1898545668c52621bced9493753be5b8138f2539542ca606732eb919081900360800190a25050505050565b613018612c70565b6000806130447f0000000000000000000000000000000000000000000000000000000000000000612aa7565b6001600160a01b0316639f51290b6040518163ffffffff1660e01b8152600401604080518083038186803b15801561307b57600080fd5b505afa15801561308f573d6000803e3d6000fd5b505050506040513d60408110156130a557600080fd5b50805160209091015190925090506130bd85846133dd565b6001600160a01b038416600090815260196020526040902054820181014211613120576040805162461bcd60e51b815260206004820152601060248201526f140e9513d7d393d517d0531313d5d15160821b604482015290519081900360640190fd5b600061312b8661201c565b1461316e576040805162461bcd60e51b815260206004820152600e60248201526d503a5245434f475f4c4f5353455360901b604482015290519081900360640190fd5b613183601561317c866120ed565b858761389b565b61243e858585613bd8565b6000818484111561321d5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156131e25781810151838201526020016131ca565b50505050905090810190601f16801561320f5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600061323033611d0d565b3360009081526008602052604081205491925090613254908363ffffffff612b8616565b336000818152600860209081526040918290208490558151868152908101849052815193945091927ffbc3a599b784fe88772fc5abcc07223f64ca0b13acc341f4fb1e46bef0510eb49281900390910190a25090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb83836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015611bd357600080fd5b60006115ac83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061318e565b600e8054600c549182905560009161338a908263ffffffff613d0416565b91505090565b6000610d577f0000000000000000000000000000000000000000000000000000000000000000600a0a6133d184670de0b6b3a764000063ffffffff61359216565b9063ffffffff613d6916565b6013546001600160a01b038316600090815260156020526040902054429161340b919063ffffffff612b8616565b111561344f576040805162461bcd60e51b815260206004820152600e60248201526d140e9195539114d7d313d0d2d15160921b604482015290519081900360640190fd5b6001600160a01b0382166000908152601b602052604090205461347582611d33856120ed565b1015611602576040805162461bcd60e51b8152602060048201526011602482015270140e925394d55197d514905394d7d09053607a1b604482015290519081900360640190fd5b6134c68282613dab565b60006135086134e3611c348460095461359290919063ffffffff16565b6001600160a01b0385166000908152600a60205260409020549063ffffffff61362c16565b6001600160a01b0384166000818152600a60209081526040918290208490558151848152915193945091927fb464de3159e090617503d0166bff9ffeecdefd42cd9dbb49f918df95a80fdea3929181900390910190a2505050565b600061356d613e52565b600d54909150613583908263ffffffff61332a16565b600d5561358e6139fa565b5090565b6000826135a157506000610d57565b828202828482816135ae57fe5b04146115ac5760405162461bcd60e51b81526004018080602001828103825260218152602001806147a26021913960400191505060405180910390fd5b806000811215610c80576040805162461bcd60e51b815260206004820152600760248201526629a6aa9d27a7a160c91b604482015290519081900360640190fd5b60008282018183128015906136415750838112155b80613656575060008312801561365657508381125b6115ac5760405162461bcd60e51b815260040180806020018281038252602181526020018061475b6021913960400191505060405180910390fd5b60008082121561358e576040805162461bcd60e51b8152602060048201526007602482015266534d493a4e454760c81b604482015290519081900360640190fd5b60006136dc610f09565b11613720576040805162461bcd60e51b815260206004820152600f60248201526e4644543a5a45524f5f535550504c5960881b604482015290519081900360640190fd5b8061372a57611d6a565b613761613735610f09565b61374983600160801b63ffffffff61359216565b8161375057fe5b60065491900463ffffffff612b8616565b60065560408051828152905133917f26536799ace2c3dbe12e638ec3ade6b4173dcf1289be0a58d51a5003015649bd919081900360200190a260065460408051918252517f1f8d7705f31c3337a080803a8ad7e71946fb88d84738879be2bf402f97156e969181900360200190a150565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a082317f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561386a57600080fd5b505afa15801561387e573d6000803e3d6000fd5b505050506040513d602081101561389457600080fd5b5051905090565b6001600160a01b038116600090815260208590526040812054908484016138c257816138f8565b6138f86138eb8686016133d1876138df428863ffffffff61332a16565b9063ffffffff61359216565b839063ffffffff612b8616565b6001600160a01b038416600081815260208981526040918290208490558151848152915193945091927ff9b842c70d79466435b46540bb988aa5c998b3243bf91c36380ddb5887c0f0e4929181900390910190a2505050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611ae7908590613ed7565b6139b88282613f88565b60006135086139d5611c348460095461359290919063ffffffff16565b6001600160a01b0385166000908152600a60205260409020549063ffffffff613d0416565b600f8054600d549182905560009161338a908263ffffffff613d0416565b6000613a22610f09565b11613a66576040805162461bcd60e51b815260206004820152600f60248201526e4644543a5a45524f5f535550504c5960881b604482015290519081900360640190fd5b80613a7057611d6a565b6000613aa9613a7d610f09565b613a9184600160801b63ffffffff61359216565b81613a9857fe5b60095491900463ffffffff612b8616565b600981905560408051848152905191925033917ff88156a8032a0d2c65df18fafaf84e0bea647b3d94a0f7fc6ab14c97dec2bf749181900360200190a26040805182815290517f240ce2b5ce9e9e5a70010c7f8034c233d89b7ce2d60f3a38d9bc3ca01a36f88c9181900360200190a15050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611605576040805162461bcd60e51b8152602060048201526009602482015268140e9393d517d1115360ba1b604482015290519081900360640190fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611020908490613ed7565b613be3838383613fd4565b6000613bfd611c348360095461359290919063ffffffff16565b6001600160a01b0385166000908152600a602052604081205491925090613c2a908363ffffffff61362c16565b6001600160a01b038087166000908152600a602052604080822084905591871681529081205491925090613c64908463ffffffff613d0416565b6001600160a01b038087166000908152600a602090815260409182902084905581518681529151939450918916927fb464de3159e090617503d0166bff9ffeecdefd42cd9dbb49f918df95a80fdea3929181900390910190a26040805182815290516001600160a01b038716917fb464de3159e090617503d0166bff9ffeecdefd42cd9dbb49f918df95a80fdea3919081900360200190a2505050505050565b6000818303818312801590613d195750838113155b80613d2e5750600083128015613d2e57508381135b6115ac5760405162461bcd60e51b81526004018080602001828103825260248152602001806148556024913960400191505060405180910390fd5b60006115ac83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614100565b613db58282614165565b6000613df7613dd2611c348460065461359290919063ffffffff16565b6001600160a01b0385166000908152600760205260409020549063ffffffff61362c16565b6001600160a01b0384166000818152600760209081526040918290208490558151848152915193945091927ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773929181900390910190a2505050565b6000613e5d3361201c565b336000908152600b602052604081205491925090613e81908363ffffffff612b8616565b336000818152600b60209081526040918290208490558151868152908101849052815193945091927f814eba35782909dbbaeefb8104073dfca45de43173f7077970c1584b3cf918b59281900390910190a25090565b6060613f2c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661426d9092919063ffffffff16565b80519091501561102057808060200190516020811015613f4b57600080fd5b50516110205760405162461bcd60e51b815260040180806020018281038252602a815260200180614879602a913960400191505060405180910390fd5b613f928282614284565b6000613df7613faf611c348460065461359290919063ffffffff16565b6001600160a01b0385166000908152600760205260409020549063ffffffff613d0416565b613fdf838383614380565b6000613ff9611c348360065461359290919063ffffffff16565b6001600160a01b03851660009081526007602052604081205491925090614026908363ffffffff61362c16565b6001600160a01b0380871660009081526007602052604080822084905591871681529081205491925090614060908463ffffffff613d0416565b6001600160a01b0380871660009081526007602090815260409182902084905581518681529151939450918916927ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773929181900390910190a26040805182815290516001600160a01b038716917ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773919081900360200190a2505050505050565b6000818361414f5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156131e25781810151838201526020016131ca565b50600083858161415b57fe5b0495945050505050565b6001600160a01b0382166141aa5760405162461bcd60e51b81526004018080602001828103825260218152602001806147eb6021913960400191505060405180910390fd5b6141b682600083611020565b6141f9816040518060600160405280602281526020016146f1602291396001600160a01b038516600090815260208190526040902054919063ffffffff61318e16565b6001600160a01b038316600090815260208190526040902055600254614225908263ffffffff61332a16565b6002556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b606061427c84846000856144e7565b949350505050565b6001600160a01b0382166142df576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6142eb60008383611020565b6002546142fe908263ffffffff612b8616565b6002556001600160a01b03821660009081526020819052604090205461432a908263ffffffff612b8616565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6001600160a01b0383166143c55760405162461bcd60e51b815260040180806020018281038252602581526020018061480c6025913960400191505060405180910390fd5b6001600160a01b03821661440a5760405162461bcd60e51b81526004018080602001828103825260238152602001806146ce6023913960400191505060405180910390fd5b614415838383611020565b61445881604051806060016040528060268152602001614735602691396001600160a01b038616600090815260208190526040902054919063ffffffff61318e16565b6001600160a01b03808516600090815260208190526040808220939093559084168152205461448d908263ffffffff612b8616565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b6060824710156145285760405162461bcd60e51b815260040180806020018281038252602681526020018061477c6026913960400191505060405180910390fd5b61453185614643565b614582576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106145c15780518252601f1990920191602091820191016145a2565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614623576040519150601f19603f3d011682016040523d82523d6000602084013e614628565b606091505b5091509150614638828286614649565b979650505050505050565b3b151590565b606083156146585750816115ac565b8251156146685782518084602001fd5b60405162461bcd60e51b81526020600482018181528451602484015284518593919283926044019190850190808383600083156131e25781810151838201526020016131ca565b6040518060e00160405280600790602082028036833750919291505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63655369676e6564536166654d6174683a206164646974696f6e206f766572666c6f77416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f20616464726573735369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656445524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220338ed6b58b0c477d8e804960099fbfd7403722cacff069fd1119edb300baf01664736f6c634300060b0033a264697066735822122015df61e79ad57513f2b84a8e0a4c417761e17b1608dbd3dee62eba1267d4e06664736f6c634300060b0033000000000000000000000000c234c62c8c09687dff0d9047e40042cd166f3600

Deployed Bytecode

0x60806040523480156200001157600080fd5b5060043610620000e25760003560e01c8063945164e71162000099578063c3124525116200006f578063c3124525146200023a578063c5ba73ed1462000244578063cc2e0a26146200024e578063f3f616c0146200027757620000e2565b8063945164e714620001de5780639f71f14a14620001fa578063ac4afa38146200021a57620000e2565b8063312b898214620000e75780633f4ba83a14620001585780635b16ebb714620001645780635c975abb14620001a15780636050abb214620001ab5780638456cb5914620001d4575b600080fd5b6200013c600480360360e0811015620000ff57600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359091169060808101359060a08101359060c00135620002a8565b604080516001600160a01b039092168252519081900360200190f35b6200016262000a23565b005b6200018d600480360360208110156200017c57600080fd5b50356001600160a01b031662000a39565b604080519115158252519081900360200190f35b6200018d62000a4e565b6200018d60048036036020811015620001c357600080fd5b50356001600160a01b031662000a57565b6200016262000a6c565b620001e862000a80565b60408051918252519081900360200190f35b6200020462000a86565b6040805160ff9092168252519081900360200190f35b6200013c600480360360208110156200023257600080fd5b503562000a8b565b6200013c62000aa6565b6200020462000ab5565b62000162600480360360208110156200026657600080fd5b50356001600160a01b031662000aba565b62000162600480360360408110156200028f57600080fd5b506001600160a01b038135169060200135151562000ae6565b6000805460ff1615620002f5576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b620002ff62000b50565b600254604080516313d459fb60e01b81523060048201526001600160a01b038881166024830152600360448301529151919092169182916313d459fb91606480820192602092909190829003018186803b1580156200035d57600080fd5b505afa15801562000372573d6000803e3d6000fd5b505050506040513d60208110156200038957600080fd5b5051620003ce576040805162461bcd60e51b815260206004820152600e60248201526d28231d24a72b20a624a22fa6262360911b604482015290519081900360640190fd5b604080516313d459fb60e01b8152306004828101919091526001600160a01b038a8116602484015260448301919091529151918316916313d459fb91606480820192602092909190829003018186803b1580156200042b57600080fd5b505afa15801562000440573d6000803e3d6000fd5b505050506040513d60208110156200045757600080fd5b50516200049c576040805162461bcd60e51b815260206004820152600e60248201526d28231d24a72b20a624a22fa9a62360911b604482015290519081900360640190fd5b60408051631b971aa360e11b815233600482015290516001600160a01b0383169163372e3546916024808301926020929190829003018186803b158015620004e357600080fd5b505afa158015620004f8573d6000803e3d6000fd5b505050506040513d60208110156200050f57600080fd5b505162000555576040805162461bcd60e51b815260206004820152600f60248201526e50463a4e4f545f44454c454741544560881b604482015290519081900360640190fd5b5060606040518060400160405280601081526020016f26b0b83632902837b7b6102a37b5b2b760811b815250905060606040518060400160405280600681526020016504d504c2d4c560d41b81525090506000338b8b8b8b8b8b8b8a8a604051620005c09062000eed565b6001600160a01b03808c1682528a81166020808401919091528a821660408401528982166060840152908816608083015260a0820187905260c0820186905260e0820185905261014061010083018181528551918401919091528451909161012084019161016085019187019080838360005b838110156200064d57818101518382015260200162000633565b50505050905090810190601f1680156200067b5780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015620006b057818101518382015260200162000696565b50505050905090810190601f168015620006de5780820380516001836020036101000a031916815260200191505b509c50505050505050505050505050604051809103906000f0801580156200070a573d6000803e3d6000fd5b5090508093508360036000600154815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600160046000866001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff02191690831515021790555060016000815460010191905081905550336001600160a01b0316846001600160a01b03167fca6c57d58de1ff84c629ea4a44b3a2f19a6c83cc1dc3cd7d1ede2067a6a1016d8d8d856001600160a01b0316639759164a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200080957600080fd5b505afa1580156200081e573d6000803e3d6000fd5b505050506040513d60208110156200083557600080fd5b5051604080516233b1cf60e41b815290516001600160a01b0389169163033b1cf0916004808301926020929190829003018186803b1580156200087757600080fd5b505afa1580156200088c573d6000803e3d6000fd5b505050506040513d6020811015620008a357600080fd5b81019080805190602001909291905050508d8d8d8c8c604051808a6001600160a01b03166001600160a01b03168152602001896001600160a01b03166001600160a01b03168152602001886001600160a01b03166001600160a01b03168152602001876001600160a01b03166001600160a01b031681526020018681526020018581526020018481526020018060200180602001838103835285818151815260200191508051906020019080838360005b838110156200096e57818101518382015260200162000954565b50505050905090810190601f1680156200099c5780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015620009d1578181015183820152602001620009b7565b50505050905090810190601f168015620009ff5780820380516001836020036101000a031916815260200191505b509b50505050505050505050505060405180910390a3505050979650505050505050565b62000a2d62000c12565b62000a3762000cfd565b565b60046020526000908152604090205460ff1681565b60005460ff1690565b60056020526000908152604090205460ff1681565b62000a7662000c12565b62000a3762000d9e565b60015481565b600481565b6003602052600090815260409020546001600160a01b031681565b6002546001600160a01b031681565b600381565b62000ac462000e22565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b62000af062000e22565b6001600160a01b038216600081815260056020908152604091829020805460ff1916851515908117909155825190815291517fc0d8ae32722eafe08b35b2bb46dec31dcbbd5bb18069f66995df3c9f47f4d64f9281900390910190a25050565b600260009054906101000a90046001600160a01b03166001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b15801562000b9f57600080fd5b505afa15801562000bb4573d6000803e3d6000fd5b505050506040513d602081101562000bcb57600080fd5b50511562000a37576040805162461bcd60e51b815260206004820152600f60248201526e14118e941493d513d7d4105554d151608a1b604482015290519081900360640190fd5b600260009054906101000a90046001600160a01b03166001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b15801562000c6157600080fd5b505afa15801562000c76573d6000803e3d6000fd5b505050506040513d602081101562000c8d57600080fd5b50516001600160a01b031633148062000cb557503360009081526005602052604090205460ff165b62000a37576040805162461bcd60e51b815260206004820152601360248201527228231d2727aa2fa3a7ab2fa7a92fa0a226a4a760691b604482015290519081900360640190fd5b60005460ff1662000d4c576040805162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015290519081900360640190fd5b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa62000d8162000ee9565b604080516001600160a01b039092168252519081900360200190a1565b60005460ff161562000dea576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25862000d8162000ee9565b600260009054906101000a90046001600160a01b03166001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b15801562000e7157600080fd5b505afa15801562000e86573d6000803e3d6000fd5b505050506040513d602081101562000e9d57600080fd5b50516001600160a01b0316331462000a37576040805162461bcd60e51b815260206004820152600a60248201526928231d2727aa2fa3a7ab60b11b604482015290519081900360640190fd5b3390565b6150438062000efc8339019056fe6101806040523480156200001257600080fd5b50604051620050433803806200504383398181016040526101408110156200003957600080fd5b815160208301516040808501516060860151608087015160a088015160c089015160e08a01516101008b0180519751999b989a969995989497939692959194919392820192846401000000008211156200009257600080fd5b908301906020820185811115620000a857600080fd5b8251640100000000811182820188101715620000c357600080fd5b82525081516020918201929091019080838360005b83811015620000f2578181015183820152602001620000d8565b50505050905090810190601f168015620001205780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200014457600080fd5b9083019060208201858111156200015a57600080fd5b82516401000000008111828201881017156200017557600080fd5b82525081516020918201929091019080838360005b83811015620001a45781810151838201526020016200018a565b50505050905090810190601f168015620001d25780820380516001836020036101000a031916815260200191505b5060405250505081818181818181818160039080519060200190620001f99291906200056e565b5080516200020f9060049060208401906200056e565b50506005805460ff1916601217905550732c1c30fb8cc313ef3cfd2e2bbf2da88add902c30955063149851689450620002589350339250506001600160e01b03620004fe169050565b604080516001600160e01b031960e085901b1681526001600160a01b039283166004820152828e166024820152918c16604483015260648201899052608482018890525160a4808301926000929190829003018186803b158015620002bc57600080fd5b505af4158015620002d1573d6000803e3d6000fd5b50505050886001600160a01b03166080816001600160a01b031660601b81525050886001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156200032c57600080fd5b505afa15801562000341573d6000803e3d6000fd5b505050506040513d60208110156200035857600080fd5b505160ff1661014052606088811b6001600160601b031990811660e0528b821b1660a052601086905561016085905233901b610120526012839055604080516342ef033f60e11b81526001600160a01b03808b1660048301528b811660248301529151918916916385de067e916044808201926020929091908290030181600087803b158015620003e857600080fd5b505af1158015620003fd573d6000803e3d6000fd5b505050506040513d60208110156200041457600080fd5b505160601b6001600160601b0319166101005260408051630cf5bc1d60e11b81526001600160a01b038b811660048301529151918816916319eb783a916024808201926020929091908290030181600087803b1580156200047457600080fd5b505af115801562000489573d6000803e3d6000fd5b505050506040513d6020811015620004a057600080fd5b505160601b6001600160601b03191660c05262ed4e00601355604080516000815290517f24b0afb747a8213aea796b9518bfa667de187b83390eda7cc93b8e57f80fcd1a916020908290030190a15050505050505050505062000613565b6000816001600160a01b031663c31245256040518163ffffffff1660e01b815260040160206040518083038186803b1580156200053a57600080fd5b505afa1580156200054f573d6000803e3d6000fd5b505050506040513d60208110156200056657600080fd5b505192915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620005b157805160ff1916838001178555620005e1565b82800160010185558215620005e1579182015b82811115620005e1578251825591602001919060010190620005c4565b50620005ef929150620005f3565b5090565b6200061091905b80821115620005ef5760008155600101620005fa565b90565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c61014051610160516148fd62000746600039806111395280611c695280612644525080613397525080610d5f5280610da85280610f5f52806119ae5280611ed152806123b15280612c755280613020525080610c875280610e6b528061127752806112f2528061139f52806114195280612e45525080610df3528061225f5280612e6d525080610f8752806112a1528061251052806127285280612c0c5280612f7552806132ac5280613805525080610e43528061124d5280611b4c5280612d4b5280613b28525080610e1b5280611027528061137552806113ea5280611efa528061238d52806127055280612be25280612dd95280612e1d5280612f5352806137d652506148fd6000f3fe608060405234801561001057600080fd5b50600436106103fb5760003560e01c806370a0823111610215578063a9059cbb11610125578063c771c390116100b8578063d82745c811610087578063d82745c814610bfa578063dd62ed3e14610c20578063ee947a7c14610c4e578063eff9884314610c56578063fec984e314610c5e576103fb565b8063c771c39014610b79578063c965b54814610b96578063cc0fef0214610bc4578063d7bd3c9114610bcc576103fb565b8063b69410de116100f4578063b69410de14610acf578063b6b55f2514610ad7578063c374682514610af4578063c59e395914610b53576103fb565b8063a9059cbb14610a4f578063ac64165514610a7b578063aed4966a14610a83578063af6d557114610aa9576103fb565b806384b76824116101a85780639759164a116101775780639759164a146109ec5780639f3c7325146109f4578063a33142f7146109fc578063a43baa3d14610a04578063a457c2d714610a23576103fb565b806384b76824146109885780638905fd4f146109905780639185192a146109b657806395d89b41146109e4576103fb565b806376687d3d116101e457806376687d3d1461093e5780637b99adb11461094657806380cd916d1461096357806380e7ce851461096b576103fb565b806370a08231146108da57806371073bac1461090057806373ef9a50146109085780637666f12514610910576103fb565b80632e1a7d4d1161031057806346c162de116102a357806351b42b001161027257806351b42b001461081c578063613384f214610824578063641ad8a91461084a5780636696779114610876578063681cb10a1461089c576103fb565b806346c162de146107de5780634bb278f3146107e65780634e97415f146107ee5780634f85221a14610814576103fb565b806340504ba0116102df57806340504ba01461074757806340bde09814610775578063410dbf7e1461079b578063443bb293146107b8576103fb565b80632e1a7d4d146106ee578063313ce5671461070b57806339509351146107135780634046af2b1461073f576103fb565b80631831ccf21161039357806323b872dd1161036257806323b872dd1461062857806324600fc31461065e57806324b92e8e1461066657806327f918561461068c5780632ac04ac8146106b8576103fb565b80631831ccf21461057a5780631aa37cec14610582578063209b2bca146105ba57806321c0b342146105c2576103fb565b80630d49b38c116103cf5780630d49b38c1461051957806313bf9e7e14610521578063174a5be41461055457806318160ddd14610572576103fb565b806241c52c14610400578063033b1cf01461043857806306fdde031461045c578063095ea7b3146104d9575b600080fd5b6104266004803603602081101561041657600080fd5b50356001600160a01b0316610c66565b60408051918252519081900360200190f35b610440610c85565b604080516001600160a01b039092168252519081900360200190f35b610464610ca9565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561049e578181015183820152602001610486565b50505050905090810190601f1680156104cb5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610505600480360360408110156104ef57600080fd5b506001600160a01b038135169060200135610d3f565b604080519115158252519081900360200190f35b610440610d5d565b610529610d81565b6040805195865260208601949094529115158484015260608401526080830152519081900360a00190f35b61055c610f04565b6040805160ff9092168252519081900360200190f35b610426610f09565b610505610f0f565b6105b86004803603606081101561059857600080fd5b506001600160a01b03813581169160208101359091169060400135610f18565b005b610440611025565b6105f0600480360360408110156105d857600080fd5b506001600160a01b0381358116916020013516611049565b604051808260e080838360005b838110156106155781810151838201526020016105fd565b5050505090500191505060405180910390f35b6105056004803603606081101561063e57600080fd5b506001600160a01b03813581169160208101359091169060400135611525565b6105b86115b3565b6104266004803603602081101561067c57600080fd5b50356001600160a01b0316611607565b6105b8600480360360408110156106a257600080fd5b506001600160a01b038135169060200135611619565b6105b8600480360360608110156106ce57600080fd5b506001600160a01b038135811691602081013590911690604001356117ba565b6105b86004803603602081101561070457600080fd5b5035611991565b61055c611aed565b6105056004803603604081101561072957600080fd5b506001600160a01b038135169060200135611af6565b610440611b4a565b6105b86004803603604081101561075d57600080fd5b506001600160a01b0381358116916020013516611b6e565b6104266004803603602081101561078b57600080fd5b50356001600160a01b0316611bef565b6105b8600480360360208110156107b157600080fd5b5035611c58565b610426600480360360208110156107ce57600080fd5b50356001600160a01b0316611d0d565b6105b8611d3f565b6105b8611d6d565b6104266004803603602081101561080457600080fd5b50356001600160a01b0316611e3b565b610505611e80565b6105b8611ea0565b6105056004803603602081101561083a57600080fd5b50356001600160a01b0316611ff9565b61085261200e565b6040518082600281111561086257fe5b60ff16815260200191505060405180910390f35b6104266004803603602081101561088c57600080fd5b50356001600160a01b031661201c565b610426600480360360808110156108b257600080fd5b506001600160a01b038135811691602081013582169160408201358116916060013516612042565b610426600480360360208110156108f057600080fd5b50356001600160a01b03166120ed565b610426612108565b6105b861210e565b6105b86004803603604081101561092657600080fd5b506001600160a01b03813516906020013515156121a4565b61042661220c565b6105b86004803603602081101561095c57600080fd5b5035612212565b61044061225d565b6105056004803603602081101561098157600080fd5b5035612281565b6105b86122d4565b6105b8600480360360208110156109a657600080fd5b50356001600160a01b0316612370565b6105b8600480360360408110156109cc57600080fd5b506001600160a01b0381351690602001351515612445565b6104646124ad565b61044061250e565b610426612532565b610426612538565b6105b860048036036020811015610a1a57600080fd5b5035151561253e565b61050560048036036040811015610a3957600080fd5b506001600160a01b03813516906020013561258d565b61050560048036036040811015610a6557600080fd5b506001600160a01b0381351690602001356125fb565b61042661260f565b61042660048036036020811015610a9957600080fd5b50356001600160a01b0316612615565b61042660048036036020811015610abf57600080fd5b50356001600160a01b0316612630565b610426612642565b6105b860048036036020811015610aed57600080fd5b5035612666565b610b3a600480360360a0811015610b0a57600080fd5b506001600160a01b03813581169160208101358216916040820135811691606081013590911690608001356127a0565b6040805192835260208301919091528051918290030190f35b61050560048036036020811015610b6957600080fd5b50356001600160a01b031661285d565b6105b860048036036020811015610b8f57600080fd5b5035612872565b61042660048036036040811015610bac57600080fd5b506001600160a01b03813581169160200135166128fa565b6105b8612917565b61044060048036036040811015610be257600080fd5b506001600160a01b0381358116916020013516612942565b61042660048036036020811015610c1057600080fd5b50356001600160a01b0316612968565b61042660048036036040811015610c3657600080fd5b506001600160a01b038135811691602001351661297a565b6104266129a5565b6104266129ab565b6104266129b1565b6001600160a01b0381166000908152600860205260409020545b919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60038054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610d355780601f10610d0a57610100808354040283529160200191610d35565b820191906000526020600020905b815481529060010190602001808311610d1857829003601f168201915b5050505050905090565b6000610d53610d4c6129b7565b84846129bb565b5060015b92915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806000806000732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3063767f5038610dcc7f0000000000000000000000000000000000000000000000000000000000000000612aa7565b6040805160e084901b6001600160e01b03191681526001600160a01b0392831660048201527f0000000000000000000000000000000000000000000000000000000000000000831660248201527f0000000000000000000000000000000000000000000000000000000000000000831660448201527f0000000000000000000000000000000000000000000000000000000000000000831660648201527f000000000000000000000000000000000000000000000000000000000000000090921660848301525160a48083019260a0929190829003018186803b158015610eb257600080fd5b505af4158015610ec6573d6000803e3d6000fd5b505050506040513d60a0811015610edc57600080fd5b5080516020820151604083015160608401516080909401519299919850965091945092509050565b600181565b60025490565b60145460ff1681565b610f20612b14565b610f2a6001612b24565b601154610f3d908263ffffffff612b8616565b6011556040805163fbecb17160e01b8152601660048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301527f00000000000000000000000000000000000000000000000000000000000000008116604483015280861660648301528416608482015260a481018390529051732c1c30fb8cc313ef3cfd2e2bbf2da88add902c309163fbecb1719160c4808301926000929190829003018186803b15801561100057600080fd5b505af4158015611014573d6000803e3d6000fd5b50505050611020612be0565b505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6110516146af565b611059612c70565b611061612d40565b6001600160a01b0380841660009081526016602090815260408083208685168452909152808220548151634e71d92d60e01b81529151931692634e71d92d9260048084019360e093929083900390910190829087803b1580156110c357600080fd5b505af11580156110d7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525060e08110156110fc57600080fd5b50601054604051633faa6c5d60e01b8152919250600091829182918291732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3091633faa6c5d9188917f00000000000000000000000000000000000000000000000000000000000000009190600401808460e08083838c5b8381101561117e578181015183820152602001611166565b50505050905001838152602001828152602001935050505060806040518083038186803b1580156111ae57600080fd5b505af41580156111c2573d6000803e3d6000fd5b505050506040513d60808110156111d857600080fd5b50805160208201516040830151606090930151601154929750909550919350909150821161120e57601180548390039055611232565b601154611224908290840363ffffffff612b8616565b601180546000909155925090505b600c54611245908263ffffffff612b8616565b600c556112727f000000000000000000000000000000000000000000000000000000000000000085612dcc565b61129c7f000000000000000000000000000000000000000000000000000000000000000084612dcc565b6112d57f00000000000000000000000000000000000000000000000000000000000000006112d0848463ffffffff612b8616565b612dcc565b60c0850151156112f0576112f0878660066020020151612e06565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166346c162de6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561134b57600080fd5b505af115801561135f573d6000803e3d6000fd5b5050505061136b611d3f565b611373612be0565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f2047d1633ff7768462ae07d28cb16e484203bfd6d85ce832494270ebcd9081a27f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a082317f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561147e57600080fd5b505afa158015611492573d6000803e3d6000fd5b505050506040513d60208110156114a857600080fd5b505160408051918252519081900360200190a36060808601516040805184815260208101869052808201929092529181018590526080810186905290516001600160a01b038916917f21280d282ce6aa29c649fd1825373d7c77892fac3f1958fd98d5ca52dd82a197919081900360a00190a25050505092915050565b6000611532848484613010565b6115a88461153e6129b7565b6115a3856040518060600160405280602881526020016147c3602891396001600160a01b038a1660009081526001602052604081209061157c6129b7565b6001600160a01b03168152602081019190915260400160002054919063ffffffff61318e16565b6129bb565b5060015b9392505050565b6115bb612c70565b60006115c5613225565b9050806115d25750611605565b6115dc33826132aa565b6115e4612be0565b600c546115f7908263ffffffff61332a16565b600c5561160261336c565b50505b565b60156020526000908152604090205481565b336000908152601a602090815260408083206001600160a01b03861684529091528120549061164e828463ffffffff612b8616565b336000908152601b602052604081205491925090611672908563ffffffff612b8616565b9050732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3063297e7bf786868461169a336120ed565b6040518563ffffffff1660e01b815260040180856001600160a01b03166001600160a01b0316815260200184815260200183815260200182815260200194505050505060006040518083038186803b1580156116f557600080fd5b505af4158015611709573d6000803e3d6000fd5b5050336000818152601a602090815260408083206001600160a01b038c16808552908352818420899055848452601b835292819020879055805189815291820188905280519295509293507f847e03d69a7075471d42285f4ac63570c10f3012d8bf736d66de2eef17aac3e892908290030190a360408051828152905133917fe7f3fb4dacbff434e6d283d891f199c48b05b1629f610bd7ddc62353e162fb16919081900360200190a25050505050565b6001600160a01b0383166000908152601a60209081526040808320338452909152812054906117ef828463ffffffff61332a16565b60408051631b4c903160e01b81526001600160a01b03808916600483015287166024820152604481018690529051919250732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3091631b4c903191606480820192600092909190829003018186803b15801561185c57600080fd5b505af4158015611870573d6000803e3d6000fd5b505050506001600160a01b0385166000818152601a602090815260408083203384528252808320859055928252601b9052908120546118af908561332a565b6001600160a01b038088166000818152601b60209081526040918290208590558151898152915194955092891693919233927ffaa022ea2cd7f14157070896fabadafe96cc4d4714eef7ae6a992a5084493ed59281900390910190a46040805184815260208101849052815133926001600160a01b038a16927f847e03d69a7075471d42285f4ac63570c10f3012d8bf736d66de2eef17aac3e8929081900390910190a360408051828152905133917fe7f3fb4dacbff434e6d283d891f199c48b05b1629f610bd7ddc62353e162fb16919081900360200190a2505050505050565b611999612c70565b60006119a482613390565b90506000806119d27f0000000000000000000000000000000000000000000000000000000000000000612aa7565b6001600160a01b0316639f51290b6040518163ffffffff1660e01b8152600401604080518083038186803b158015611a0957600080fd5b505afa158015611a1d573d6000803e3d6000fd5b505050506040513d6040811015611a3357600080fd5b5080516020909101519092509050611a4b33846133dd565b3360009081526019602052604090205482014203811015611aac576040805162461bcd60e51b8152602060048201526016602482015275140e95d2551211149055d7d393d517d0531313d5d15160521b604482015290519081900360640190fd5b611ab633846134bc565b611abe6115b3565b611adf33611ada611acd613563565b879063ffffffff61332a16565b6132aa565b611ae7612be0565b50505050565b60055460ff1690565b6000610d53611b036129b7565b846115a38560016000611b146129b7565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff612b8616565b7f000000000000000000000000000000000000000000000000000000000000000081565b611b76612b14565b6001600160a01b038083166000908152601660209081526040808320858516845290915280822054815163175f832960e01b8152915193169263175f83299260048084019391929182900301818387803b158015611bd357600080fd5b505af1158015611be7573d6000803e3d6000fd5b505050505050565b6001600160a01b0381166000908152600a6020526040812054600160801b90611c4a90611c4590611c39611c34611c25886120ed565b6009549063ffffffff61359216565b6135eb565b9063ffffffff61362c16565b613691565b81611c5157fe5b0492915050565b611c60612b14565b612710611c93827f000000000000000000000000000000000000000000000000000000000000000063ffffffff612b8616565b1115611cd2576040805162461bcd60e51b8152602060048201526009602482015268503a4241445f46454560b81b604482015290519081900360640190fd5b60108190556040805182815290517f9408bb8c08d29b335e36090045074610352365476d9df02e203c25db4fcd67c09181900360200190a150565b6001600160a01b038116600090815260086020526040812054610d5790611d3384611e3b565b9063ffffffff61332a16565b6000611d4961336c565b905060008113611d595750611605565b611d6a611d6582613691565b6136d2565b50565b611d75612b14565b611d7f6000612b24565b6000611d89610d81565b50509250505080611dd1576040805162461bcd60e51b815260206004820152600d60248201526c503a494e5355465f5354414b4560981b604482015290519081900360640190fd5b601480546001919061ff0019166101008302179055506014546040517f24b0afb747a8213aea796b9518bfa667de187b83390eda7cc93b8e57f80fcd1a91610100900460ff169080826002811115611e2557fe5b60ff16815260200191505060405180910390a150565b6001600160a01b038116600090815260076020526040812054600160801b90611c4a90611c4590611c39611c34611e71886120ed565b6006549063ffffffff61359216565b60006001601454610100900460ff166002811115611e9a57fe5b14905090565b611ea8612b14565b611eb26001612b24565b732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3063abbedb40611ef57f0000000000000000000000000000000000000000000000000000000000000000612aa7565b6011547f00000000000000000000000000000000000000000000000000000000000000006040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001838152602001826001600160a01b03166001600160a01b03168152602001935050505060006040518083038186803b158015611f7f57600080fd5b505af4158015611f93573d6000803e3d6000fd5b50506014805461ff00191661020017908190556040517f24b0afb747a8213aea796b9518bfa667de187b83390eda7cc93b8e57f80fcd1a935061010090910460ff16915080826002811115611fe457fe5b60ff16815260200191505060405180910390a1565b60176020526000908152604090205460ff1681565b601454610100900460ff1681565b6001600160a01b0381166000908152600b6020526040812054610d5790611d3384611bef565b6040805163340e588560e11b81526001600160a01b03808716600483015280861660248301528085166044830152831660648201529051600091732c1c30fb8cc313ef3cfd2e2bbf2da88add902c309163681cb10a91608480820192602092909190829003018186803b1580156120b857600080fd5b505af41580156120cc573d6000803e3d6000fd5b505050506040513d60208110156120e257600080fd5b505195945050505050565b6001600160a01b031660009081526020819052604090205490565b600c5481565b6000612119336120ed565b1415612159576040805162461bcd60e51b815260206004820152600a602482015269140e96915493d7d0905360b21b604482015290519081900360640190fd5b336000818152601960209081526040918290204290819055825190815291517f8a05f911d8ab7fc50fec37ef4ba7f9bfcb1a3c191c81dcd824ad0946c4e20d659281900390910190a2565b6121ac612b14565b6001600160a01b038216600081815260186020908152604091829020805460ff1916851515908117909155825190815291517fdf56132520665b33cd5731c5cfbacd8bee82524e67df563bb25b2be304f91d449281900390910190a25050565b60125481565b61221a612c70565b612222612d40565b60128190556040805182815290517f3ff20538222f568f27ff436c0c49dfd3e48d5b8f86533a3f759dc1c7089775ab9181900360200190a150565b7f000000000000000000000000000000000000000000000000000000000000000081565b60145460009060ff16806122a457503360009081526018602052604090205460ff165b8015610d5757506012546122cc836122c06011546122c06137d2565b9063ffffffff612b8616565b111592915050565b33600090815260196020526040902054612329576040805162461bcd60e51b8152602060048201526011602482015270503a4e4f545f5749544844524157494e4760781b604482015290519081900360640190fd5b3360008181526019602090815260408083208390558051928352517f8a05f911d8ab7fc50fec37ef4ba7f9bfcb1a3c191c81dcd824ad0946c4e20d659281900390910190a2565b732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3063a89d5ddb827f00000000000000000000000000000000000000000000000000000000000000006123d57f0000000000000000000000000000000000000000000000000000000000000000612aa7565b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015292841660248401529216604482015290516064808301926000929190829003018186803b15801561242a57600080fd5b505af415801561243e573d6000803e3d6000fd5b5050505050565b61244d612b14565b6001600160a01b038216600081815260176020908152604091829020805460ff1916851515908117909155825190815291517f353578bbc0ab907b7018b0f7b50b5f822d31dc9fcf4c16fffa780e109ca7c9309281900390910190a25050565b60048054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610d355780601f10610d0a57610100808354040283529160200191610d35565b7f000000000000000000000000000000000000000000000000000000000000000081565b600e5481565b600d5481565b612546612b14565b6014805482151560ff19909116811790915560408051918252517feeba6fd794e30165023f7e3d017e92901622076a95d36e45906955e025ff4fe79181900360200190a150565b6000610d5361259a6129b7565b846115a3856040518060600160405280602581526020016148a360259139600160006125c46129b7565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff61318e16565b6000610d536126086129b7565b8484613010565b60115481565b6001600160a01b03166000908152600b602052604090205490565b601b6020526000908152604090205481565b7f000000000000000000000000000000000000000000000000000000000000000081565b61266e612c70565b6126786001612b24565b61268181612281565b6126c6576040805162461bcd60e51b8152602060048201526011602482015270140e91115417d393d517d0531313d5d151607a1b604482015290519081900360640190fd5b3360009081526019602052604081208190556126e182613390565b90506126f860156126f1336120ed565b833361389b565b6127536001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016337f00000000000000000000000000000000000000000000000000000000000000008563ffffffff61395416565b61275d33826139ae565b612765612be0565b6040805160008152905133917f8a05f911d8ab7fc50fec37ef4ba7f9bfcb1a3c191c81dcd824ad0946c4e20d65919081900360200190a25050565b6040805163c374682560e01b81526001600160a01b03808816600483015280871660248301528086166044830152841660648201526084810183905281516000928392732c1c30fb8cc313ef3cfd2e2bbf2da88add902c309263c37468259260a480840193919291829003018186803b15801561281c57600080fd5b505af4158015612830573d6000803e3d6000fd5b505050506040513d604081101561284657600080fd5b508051602090910151909890975095505050505050565b60186020526000908152604090205460ff1681565b61287a612b14565b6013548111156128bf576040805162461bcd60e51b815260206004820152600b60248201526a503a4241445f56414c554560a81b604482015290519081900360640190fd5b60138190556040805182815290517f3094b4ce0463766c3cd81ed2ae2451610dcac39a1061fa023ca9d3d4df959f759181900360200190a150565b601a60209081526000928352604080842090915290825290205481565b60006129216139fa565b9050600081136129315750611605565b611d6a61293d82613691565b613a18565b60166020908152600092835260408084209091529082529020546001600160a01b031681565b60196020526000908152604090205481565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60135481565b60105481565b600f5481565b3390565b6001600160a01b038316612a005760405162461bcd60e51b81526004018080602001828103825260248152602001806148316024913960400191505060405180910390fd5b6001600160a01b038216612a455760405162461bcd60e51b81526004018080602001828103825260228152602001806147136022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6000816001600160a01b031663c31245256040518163ffffffff1660e01b815260040160206040518083038186803b158015612ae257600080fd5b505afa158015612af6573d6000803e3d6000fd5b505050506040513d6020811015612b0c57600080fd5b505192915050565b612b1c613b1d565b611605612c70565b806002811115612b3057fe5b601454610100900460ff166002811115612b4657fe5b14611d6a576040805162461bcd60e51b815260206004820152600b60248201526a503a4241445f535441544560a81b604482015290519081900360640190fd5b6000828201838110156115ac576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f2047d1633ff7768462ae07d28cb16e484203bfd6d85ce832494270ebcd9081a2612c5d6137d2565b60408051918252519081900360200190a3565b612c997f0000000000000000000000000000000000000000000000000000000000000000612aa7565b6001600160a01b031663425fad586040518163ffffffff1660e01b815260040160206040518083038186803b158015612cd157600080fd5b505afa158015612ce5573d6000803e3d6000fd5b505050506040513d6020811015612cfb57600080fd5b505115611605576040805162461bcd60e51b815260206004820152600e60248201526d140e941493d513d7d4105554d15160921b604482015290519081900360640190fd5b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480612d8657503360009081526017602052604090205460ff165b611605576040805162461bcd60e51b8152602060048201526012602482015271281d2727aa2fa222a62fa7a92fa0a226a4a760711b604482015290519081900360640190fd5b6116026001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016838363ffffffff613b8616565b6040805162715b0960e41b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000811660248301527f000000000000000000000000000000000000000000000000000000000000000016604482015260648101839052905160009182918291732c1c30fb8cc313ef3cfd2e2bbf2da88add902c3091630715b09091608480820192606092909190829003018186803b158015612ede57600080fd5b505af4158015612ef2573d6000803e3d6000fd5b505050506040513d6060811015612f0857600080fd5b5080516020820151604090920151909450909250905080841115612f4657600d54612f3b9082860363ffffffff612b8616565b600d55612f46612917565b612fa06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000008363ffffffff613b8616565b601154612fb3908563ffffffff61332a16565b60115560408051858152602081018590528082018490526060810183905290516001600160a01b038716917fd393d18014c1898545668c52621bced9493753be5b8138f2539542ca606732eb919081900360800190a25050505050565b613018612c70565b6000806130447f0000000000000000000000000000000000000000000000000000000000000000612aa7565b6001600160a01b0316639f51290b6040518163ffffffff1660e01b8152600401604080518083038186803b15801561307b57600080fd5b505afa15801561308f573d6000803e3d6000fd5b505050506040513d60408110156130a557600080fd5b50805160209091015190925090506130bd85846133dd565b6001600160a01b038416600090815260196020526040902054820181014211613120576040805162461bcd60e51b815260206004820152601060248201526f140e9513d7d393d517d0531313d5d15160821b604482015290519081900360640190fd5b600061312b8661201c565b1461316e576040805162461bcd60e51b815260206004820152600e60248201526d503a5245434f475f4c4f5353455360901b604482015290519081900360640190fd5b613183601561317c866120ed565b858761389b565b61243e858585613bd8565b6000818484111561321d5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156131e25781810151838201526020016131ca565b50505050905090810190601f16801561320f5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600061323033611d0d565b3360009081526008602052604081205491925090613254908363ffffffff612b8616565b336000818152600860209081526040918290208490558151868152908101849052815193945091927ffbc3a599b784fe88772fc5abcc07223f64ca0b13acc341f4fb1e46bef0510eb49281900390910190a25090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb83836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015611bd357600080fd5b60006115ac83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061318e565b600e8054600c549182905560009161338a908263ffffffff613d0416565b91505090565b6000610d577f0000000000000000000000000000000000000000000000000000000000000000600a0a6133d184670de0b6b3a764000063ffffffff61359216565b9063ffffffff613d6916565b6013546001600160a01b038316600090815260156020526040902054429161340b919063ffffffff612b8616565b111561344f576040805162461bcd60e51b815260206004820152600e60248201526d140e9195539114d7d313d0d2d15160921b604482015290519081900360640190fd5b6001600160a01b0382166000908152601b602052604090205461347582611d33856120ed565b1015611602576040805162461bcd60e51b8152602060048201526011602482015270140e925394d55197d514905394d7d09053607a1b604482015290519081900360640190fd5b6134c68282613dab565b60006135086134e3611c348460095461359290919063ffffffff16565b6001600160a01b0385166000908152600a60205260409020549063ffffffff61362c16565b6001600160a01b0384166000818152600a60209081526040918290208490558151848152915193945091927fb464de3159e090617503d0166bff9ffeecdefd42cd9dbb49f918df95a80fdea3929181900390910190a2505050565b600061356d613e52565b600d54909150613583908263ffffffff61332a16565b600d5561358e6139fa565b5090565b6000826135a157506000610d57565b828202828482816135ae57fe5b04146115ac5760405162461bcd60e51b81526004018080602001828103825260218152602001806147a26021913960400191505060405180910390fd5b806000811215610c80576040805162461bcd60e51b815260206004820152600760248201526629a6aa9d27a7a160c91b604482015290519081900360640190fd5b60008282018183128015906136415750838112155b80613656575060008312801561365657508381125b6115ac5760405162461bcd60e51b815260040180806020018281038252602181526020018061475b6021913960400191505060405180910390fd5b60008082121561358e576040805162461bcd60e51b8152602060048201526007602482015266534d493a4e454760c81b604482015290519081900360640190fd5b60006136dc610f09565b11613720576040805162461bcd60e51b815260206004820152600f60248201526e4644543a5a45524f5f535550504c5960881b604482015290519081900360640190fd5b8061372a57611d6a565b613761613735610f09565b61374983600160801b63ffffffff61359216565b8161375057fe5b60065491900463ffffffff612b8616565b60065560408051828152905133917f26536799ace2c3dbe12e638ec3ade6b4173dcf1289be0a58d51a5003015649bd919081900360200190a260065460408051918252517f1f8d7705f31c3337a080803a8ad7e71946fb88d84738879be2bf402f97156e969181900360200190a150565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a082317f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561386a57600080fd5b505afa15801561387e573d6000803e3d6000fd5b505050506040513d602081101561389457600080fd5b5051905090565b6001600160a01b038116600090815260208590526040812054908484016138c257816138f8565b6138f86138eb8686016133d1876138df428863ffffffff61332a16565b9063ffffffff61359216565b839063ffffffff612b8616565b6001600160a01b038416600081815260208981526040918290208490558151848152915193945091927ff9b842c70d79466435b46540bb988aa5c998b3243bf91c36380ddb5887c0f0e4929181900390910190a2505050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611ae7908590613ed7565b6139b88282613f88565b60006135086139d5611c348460095461359290919063ffffffff16565b6001600160a01b0385166000908152600a60205260409020549063ffffffff613d0416565b600f8054600d549182905560009161338a908263ffffffff613d0416565b6000613a22610f09565b11613a66576040805162461bcd60e51b815260206004820152600f60248201526e4644543a5a45524f5f535550504c5960881b604482015290519081900360640190fd5b80613a7057611d6a565b6000613aa9613a7d610f09565b613a9184600160801b63ffffffff61359216565b81613a9857fe5b60095491900463ffffffff612b8616565b600981905560408051848152905191925033917ff88156a8032a0d2c65df18fafaf84e0bea647b3d94a0f7fc6ab14c97dec2bf749181900360200190a26040805182815290517f240ce2b5ce9e9e5a70010c7f8034c233d89b7ce2d60f3a38d9bc3ca01a36f88c9181900360200190a15050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611605576040805162461bcd60e51b8152602060048201526009602482015268140e9393d517d1115360ba1b604482015290519081900360640190fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611020908490613ed7565b613be3838383613fd4565b6000613bfd611c348360095461359290919063ffffffff16565b6001600160a01b0385166000908152600a602052604081205491925090613c2a908363ffffffff61362c16565b6001600160a01b038087166000908152600a602052604080822084905591871681529081205491925090613c64908463ffffffff613d0416565b6001600160a01b038087166000908152600a602090815260409182902084905581518681529151939450918916927fb464de3159e090617503d0166bff9ffeecdefd42cd9dbb49f918df95a80fdea3929181900390910190a26040805182815290516001600160a01b038716917fb464de3159e090617503d0166bff9ffeecdefd42cd9dbb49f918df95a80fdea3919081900360200190a2505050505050565b6000818303818312801590613d195750838113155b80613d2e5750600083128015613d2e57508381135b6115ac5760405162461bcd60e51b81526004018080602001828103825260248152602001806148556024913960400191505060405180910390fd5b60006115ac83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614100565b613db58282614165565b6000613df7613dd2611c348460065461359290919063ffffffff16565b6001600160a01b0385166000908152600760205260409020549063ffffffff61362c16565b6001600160a01b0384166000818152600760209081526040918290208490558151848152915193945091927ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773929181900390910190a2505050565b6000613e5d3361201c565b336000908152600b602052604081205491925090613e81908363ffffffff612b8616565b336000818152600b60209081526040918290208490558151868152908101849052815193945091927f814eba35782909dbbaeefb8104073dfca45de43173f7077970c1584b3cf918b59281900390910190a25090565b6060613f2c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661426d9092919063ffffffff16565b80519091501561102057808060200190516020811015613f4b57600080fd5b50516110205760405162461bcd60e51b815260040180806020018281038252602a815260200180614879602a913960400191505060405180910390fd5b613f928282614284565b6000613df7613faf611c348460065461359290919063ffffffff16565b6001600160a01b0385166000908152600760205260409020549063ffffffff613d0416565b613fdf838383614380565b6000613ff9611c348360065461359290919063ffffffff16565b6001600160a01b03851660009081526007602052604081205491925090614026908363ffffffff61362c16565b6001600160a01b0380871660009081526007602052604080822084905591871681529081205491925090614060908463ffffffff613d0416565b6001600160a01b0380871660009081526007602090815260409182902084905581518681529151939450918916927ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773929181900390910190a26040805182815290516001600160a01b038716917ff694bebd33ada288ae2f4485315db76739e2d5501daf315e71c9d8f841aa7773919081900360200190a2505050505050565b6000818361414f5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156131e25781810151838201526020016131ca565b50600083858161415b57fe5b0495945050505050565b6001600160a01b0382166141aa5760405162461bcd60e51b81526004018080602001828103825260218152602001806147eb6021913960400191505060405180910390fd5b6141b682600083611020565b6141f9816040518060600160405280602281526020016146f1602291396001600160a01b038516600090815260208190526040902054919063ffffffff61318e16565b6001600160a01b038316600090815260208190526040902055600254614225908263ffffffff61332a16565b6002556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b606061427c84846000856144e7565b949350505050565b6001600160a01b0382166142df576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6142eb60008383611020565b6002546142fe908263ffffffff612b8616565b6002556001600160a01b03821660009081526020819052604090205461432a908263ffffffff612b8616565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6001600160a01b0383166143c55760405162461bcd60e51b815260040180806020018281038252602581526020018061480c6025913960400191505060405180910390fd5b6001600160a01b03821661440a5760405162461bcd60e51b81526004018080602001828103825260238152602001806146ce6023913960400191505060405180910390fd5b614415838383611020565b61445881604051806060016040528060268152602001614735602691396001600160a01b038616600090815260208190526040902054919063ffffffff61318e16565b6001600160a01b03808516600090815260208190526040808220939093559084168152205461448d908263ffffffff612b8616565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b6060824710156145285760405162461bcd60e51b815260040180806020018281038252602681526020018061477c6026913960400191505060405180910390fd5b61453185614643565b614582576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106145c15780518252601f1990920191602091820191016145a2565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614623576040519150601f19603f3d011682016040523d82523d6000602084013e614628565b606091505b5091509150614638828286614649565b979650505050505050565b3b151590565b606083156146585750816115ac565b8251156146685782518084602001fd5b60405162461bcd60e51b81526020600482018181528451602484015284518593919283926044019190850190808383600083156131e25781810151838201526020016131ca565b6040518060e00160405280600790602082028036833750919291505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63655369676e6564536166654d6174683a206164646974696f6e206f766572666c6f77416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f20616464726573735369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656445524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220338ed6b58b0c477d8e804960099fbfd7403722cacff069fd1119edb300baf01664736f6c634300060b0033a264697066735822122015df61e79ad57513f2b84a8e0a4c417761e17b1608dbd3dee62eba1267d4e06664736f6c634300060b0033

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

000000000000000000000000c234c62c8c09687dff0d9047e40042cd166f3600

-----Decoded View---------------
Arg [0] : _globals (address): 0xC234c62c8C09687DFf0d9047e40042cd166F3600

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000c234c62c8c09687dff0d9047e40042cd166f3600


Deployed Bytecode Sourcemap

130230:5810:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;132589:1648;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;132589:1648:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;132589:1648:0;;;;;;;;;;;;;;135238:110;;;:::i;:::-;;130723:41;;;;;;;;;;;;;;;;-1:-1:-1;130723:41:0;-1:-1:-1;;;;;130723:41:0;;:::i;:::-;;;;;;;;;;;;;;;;;;128838:78;;;:::i;130840:52::-;;;;;;;;;;;;;;;;-1:-1:-1;130840:52:0;-1:-1:-1;;;;;130840:52:0;;:::i;134957:106::-;;;:::i;130448:28::-;;;:::i;:::-;;;;;;;;;;;;;;;;130361:36;;;:::i;:::-;;;;;;;;;;;;;;;;;;;130594:40;;;;;;;;;;;;;;;;-1:-1:-1;130594:40:0;;:::i;130528:28::-;;;:::i;130272:36::-;;;:::i;131716:132::-;;;;;;;;;;;;;;;;-1:-1:-1;131716:132:0;-1:-1:-1;;;;;131716:132:0;;:::i;134554:233::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;134554:233:0;;;;;;;;;;:::i;132589:1648::-;132856:19;129156:7;;;;129155:8;129147:37;;;;;-1:-1:-1;;;129147:37:0;;;;;;;;;;;;-1:-1:-1;;;129147:37:0;;;;;;;;;;;;;;;132888:24:::1;:22;:24::i;:::-;132963:7;::::0;132993:64:::1;::::0;;-1:-1:-1;;;132993:64:0;;133028:4:::1;132993:64;::::0;::::1;::::0;-1:-1:-1;;;;;132993:64:0;;::::1;::::0;;;;130307:1:::1;132993:64:::0;;;;;;132963:7;;;::::1;::::0;;;132993:26:::1;::::0;:64;;;;;::::1;::::0;;;;;;;;;132963:7;132993:64;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;132993:64:0;132985:91:::1;;;::::0;;-1:-1:-1;;;132985:91:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;132985:91:0;;;;;;;;;;;;;::::1;;133099:64;::::0;;-1:-1:-1;;;133099:64:0;;133134:4:::1;130396:1;133099:64:::0;;::::1;::::0;;;;-1:-1:-1;;;;;133099:64:0;;::::1;::::0;;;;;;;;;;;;;:26;;::::1;::::0;::::1;::::0;:64;;;;;::::1;::::0;;;;;;;;;:26;:64;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;133099:64:0;133091:91:::1;;;::::0;;-1:-1:-1;;;133091:91:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;133091:91:0;;;;;;;;;;;;;::::1;;133205:40;::::0;;-1:-1:-1;;;133205:40:0;;133234:10:::1;133205:40;::::0;::::1;::::0;;;-1:-1:-1;;;;;133205:28:0;::::1;::::0;::::1;::::0;:40;;;;;::::1;::::0;;;;;;;;:28;:40;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;133205:40:0;133197:92:::1;;;::::0;;-1:-1:-1;;;133197:92:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;133197:92:0;;;;;;;;;;;;;::::1;;129195:1;133313:18;:41;;;;;;;;;;;;;-1:-1:-1::0;;;133313:41:0::1;;::::0;::::1;;133365:20;:31;;;;;;;;;;;;;-1:-1:-1::0;;;133365:31:0::1;;::::0;::::1;;133409:9;133461:10;133490:14;133523:10;133552:9;133580;133608:10;133637:11;133667:12;133698:4;133721:6;133434:308;;;;;:::i;:::-;-1:-1:-1::0;;;;;133434:308:0;;::::1;::::0;;;;::::1;;::::0;;::::1;::::0;;;;;;::::1;::::0;;;;;;::::1;::::0;;;;;;::::1;::::0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::1;::::0;;;;-1:-1:-1;133434:308:0::1;;;;;;;::::0;;::::1;::::0;;;::::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;133434:308:0;;::::1;::::0;;;;;;;;::::1;::::0;;::::1;::::0;;::::1;::::0;;;;::::1;;;;;;;;::::0;;::::1;::::0;;;::::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;133409:333;;133785:4;133755:35;;133823:11;133801:5;:19;133807:12;;133801:19;;;;;;;;;;;;:33;;;;;-1:-1:-1::0;;;;;133801:33:0::1;;;;;-1:-1:-1::0;;;;;133801:33:0::1;;;;;;133867:4;133845:6;:19;133852:11;-1:-1:-1::0;;;;;133845:19:0::1;-1:-1:-1::0;;;;;133845:19:0::1;;;;;;;;;;;;;:26;;;;;;;;;;;;;;;;;;133884:12;;133882:14;;;;;;;;;;;133966:10;-1:-1:-1::0;;;;;133914:315:0::1;133940:11;-1:-1:-1::0;;;;;133914:315:0::1;;133991:14;134020:10;134045:4;-1:-1:-1::0;;;;;134045:20:0::1;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;134045:22:0;134082:18:::1;::::0;;-1:-1:-1;;;134082:18:0;;;;-1:-1:-1;;;;;134082:16:0;::::1;::::0;::::1;::::0;:18:::1;::::0;;::::1;::::0;134045:22:::1;::::0;134082:18;;;;;;;:16;:18;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;134115:10;134140:11;134166:12;134193:4;134212:6;133914:315;;;;-1:-1:-1::0;;;;;133914:315:0::1;-1:-1:-1::0;;;;;133914:315:0::1;;;;;;-1:-1:-1::0;;;;;133914:315:0::1;-1:-1:-1::0;;;;;133914:315:0::1;;;;;;-1:-1:-1::0;;;;;133914:315:0::1;-1:-1:-1::0;;;;;133914:315:0::1;;;;;;-1:-1:-1::0;;;;;133914:315:0::1;-1:-1:-1::0;;;;;133914:315:0::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;;::::1;::::0;;;::::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;133914:315:0;;::::1;::::0;;;;;;;;::::1;::::0;;::::1;::::0;;::::1;::::0;;;;::::1;;;;;;;;::::0;;::::1;::::0;;;::::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;129195:1;;;132589:1648:::0;;;;;;;;;:::o;135238:110::-;135277:36;:34;:36::i;:::-;135324:16;:14;:16::i;:::-;135238:110::o;130723:41::-;;;;;;;;;;;;;;;:::o;128838:78::-;128877:4;128901:7;;;128838:78;:::o;130840:52::-;;;;;;;;;;;;;;;:::o;134957:106::-;134994:36;:34;:36::i;:::-;135041:14;:12;:14::i;130448:28::-;;;;:::o;130361:36::-;130396:1;130361:36;:::o;130594:40::-;;;;;;;;;;;;-1:-1:-1;;;;;130594:40:0;;:::o;130528:28::-;;;-1:-1:-1;;;;;130528:28:0;;:::o;130272:36::-;130307:1;130272:36;:::o;131716:132::-;131776:18;:16;:18::i;:::-;131805:7;:35;;-1:-1:-1;;;;;;131805:35:0;-1:-1:-1;;;;;131805:35:0;;;;;;;;;;131716:132::o;134554:233::-;134643:18;:16;:18::i;:::-;-1:-1:-1;;;;;134672:35:0;;;;;;:17;:35;;;;;;;;;:45;;-1:-1:-1;;134672:45:0;;;;;;;;;;134733:46;;;;;;;;;;;;;;;;;134554:233;;:::o;135915:120::-;135983:7;;;;;;;;;-1:-1:-1;;;;;135983:7:0;-1:-1:-1;;;;;135983:22:0;;:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;135983:24:0;135982:25;135974:53;;;;;-1:-1:-1;;;135974:53:0;;;;;;;;;;;;-1:-1:-1;;;135974:53:0;;;;;;;;;;;;;;135649:176;135742:7;;;;;;;;;-1:-1:-1;;;;;135742:7:0;-1:-1:-1;;;;;135742:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;135742:18:0;-1:-1:-1;;;;;135728:32:0;:10;:32;;:65;;-1:-1:-1;135782:10:0;135764:29;;;;:17;:29;;;;;;;;135728:65;135720:97;;;;;-1:-1:-1;;;135720:97:0;;;;;;;;;;;;-1:-1:-1;;;135720:97:0;;;;;;;;;;;;;;129887:120;129432:7;;;;129424:40;;;;;-1:-1:-1;;;129424:40:0;;;;;;;;;;;;-1:-1:-1;;;129424:40:0;;;;;;;;;;;;;;;129956:5:::1;129946:15:::0;;-1:-1:-1;;129946:15:0::1;::::0;;129977:22:::1;129986:12;:10;:12::i;:::-;129977:22;::::0;;-1:-1:-1;;;;;129977:22:0;;::::1;::::0;;;;;;;::::1;::::0;;::::1;129887:120::o:0;129628:118::-;129156:7;;;;129155:8;129147:37;;;;;-1:-1:-1;;;129147:37:0;;;;;;;;;;;;-1:-1:-1;;;129147:37:0;;;;;;;;;;;;;;;129688:7:::1;:14:::0;;-1:-1:-1;;129688:14:0::1;129698:4;129688:14;::::0;;129718:20:::1;129725:12;:10;:12::i;135429:116::-:0;135504:7;;;;;;;;;-1:-1:-1;;;;;135504:7:0;-1:-1:-1;;;;;135504:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;135504:18:0;-1:-1:-1;;;;;135490:32:0;:10;:32;135482:55;;;;;-1:-1:-1;;;135482:55:0;;;;;;;;;;;;-1:-1:-1;;;135482:55:0;;;;;;;;;;;;;;64021:106;64109:10;64021:106;:::o;-1:-1:-1:-;;;;;;;;:::o

Swarm Source

ipfs://15df61e79ad57513f2b84a8e0a4c417761e17b1608dbd3dee62eba1267d4e066

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ 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.