Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
10878959 | 1538 days ago | 1 wei | ||||
10878959 | 1538 days ago | 1 wei | ||||
10849650 | 1542 days ago | 7.6069429 ETH | ||||
10849650 | 1542 days ago | 7.6069429 ETH | ||||
10813370 | 1548 days ago | 200 ETH | ||||
10813370 | 1548 days ago | 200 ETH | ||||
10765794 | 1555 days ago | 1.76520306 ETH | ||||
10765794 | 1555 days ago | 1.76520306 ETH | ||||
10708668 | 1564 days ago | 24.1679082 ETH | ||||
10708668 | 1564 days ago | 1.27199516 ETH | ||||
10708668 | 1564 days ago | 25.43990337 ETH | ||||
10708668 | 1564 days ago | 25.43990337 ETH | ||||
10708668 | 1564 days ago | 25.43990337 ETH | ||||
10698036 | 1566 days ago | 0.89 ETH | ||||
10698036 | 1566 days ago | 0.89 ETH | ||||
10682789 | 1568 days ago | 1.03031249 ETH | ||||
10682789 | 1568 days ago | 1.03031249 ETH | ||||
10662675 | 1571 days ago | 0.50229869 ETH | ||||
10662675 | 1571 days ago | 0.50229869 ETH | ||||
10660223 | 1571 days ago | 27.5 ETH | ||||
10660223 | 1571 days ago | 27.5 ETH | ||||
10648775 | 1573 days ago | 0.53566948 ETH | ||||
10648775 | 1573 days ago | 0.53566948 ETH | ||||
10648461 | 1573 days ago | 4 ETH | ||||
10648461 | 1573 days ago | 4 ETH |
Loading...
Loading
Contract Name:
OneSplit
Compiler Version
v0.5.17+commit.d19bba13
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2020-08-06 */ // File: @openzeppelin/contracts/token/ERC20/IERC20.sol pragma solidity ^0.5.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. Does not include * the optional functions; to access them see {ERC20Detailed}. */ 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); } // File: contracts/IOneSplit.sol pragma solidity ^0.5.0; // // [ msg.sender ] // | | // | | // \_/ // +---------------+ ________________________________ // | OneSplitAudit | _______________________________ \ // +---------------+ \ \ // | | ______________ | | (staticcall) // | | / ____________ \ | | // | | (call) / / \ \ | | // | | / / | | | | // \_/ | | \_/ \_/ // +--------------+ | | +----------------------+ // | OneSplitWrap | | | | OneSplitViewWrap | // +--------------+ | | +----------------------+ // | | | | | | // | | (delegatecall) | | (staticcall) | | (staticcall) // \_/ | | \_/ // +--------------+ | | +------------------+ // | OneSplit | | | | OneSplitView | // +--------------+ | | +------------------+ // | | / / // \ \________________/ / // \__________________/ // contract IOneSplitConsts { // flags = FLAG_DISABLE_UNISWAP + FLAG_DISABLE_BANCOR + ... uint256 internal constant FLAG_DISABLE_UNISWAP = 0x01; uint256 internal constant DEPRECATED_FLAG_DISABLE_KYBER = 0x02; // Deprecated uint256 internal constant FLAG_DISABLE_BANCOR = 0x04; uint256 internal constant FLAG_DISABLE_OASIS = 0x08; uint256 internal constant FLAG_DISABLE_COMPOUND = 0x10; uint256 internal constant FLAG_DISABLE_FULCRUM = 0x20; uint256 internal constant FLAG_DISABLE_CHAI = 0x40; uint256 internal constant FLAG_DISABLE_AAVE = 0x80; uint256 internal constant FLAG_DISABLE_SMART_TOKEN = 0x100; uint256 internal constant DEPRECATED_FLAG_ENABLE_MULTI_PATH_ETH = 0x200; // Deprecated, Turned off by default uint256 internal constant FLAG_DISABLE_BDAI = 0x400; uint256 internal constant FLAG_DISABLE_IEARN = 0x800; uint256 internal constant FLAG_DISABLE_CURVE_COMPOUND = 0x1000; uint256 internal constant FLAG_DISABLE_CURVE_USDT = 0x2000; uint256 internal constant FLAG_DISABLE_CURVE_Y = 0x4000; uint256 internal constant FLAG_DISABLE_CURVE_BINANCE = 0x8000; uint256 internal constant DEPRECATED_FLAG_ENABLE_MULTI_PATH_DAI = 0x10000; // Deprecated, Turned off by default uint256 internal constant DEPRECATED_FLAG_ENABLE_MULTI_PATH_USDC = 0x20000; // Deprecated, Turned off by default uint256 internal constant FLAG_DISABLE_CURVE_SYNTHETIX = 0x40000; uint256 internal constant FLAG_DISABLE_WETH = 0x80000; uint256 internal constant FLAG_DISABLE_UNISWAP_COMPOUND = 0x100000; // Works only when one of assets is ETH or FLAG_ENABLE_MULTI_PATH_ETH uint256 internal constant FLAG_DISABLE_UNISWAP_CHAI = 0x200000; // Works only when ETH<>DAI or FLAG_ENABLE_MULTI_PATH_ETH uint256 internal constant FLAG_DISABLE_UNISWAP_AAVE = 0x400000; // Works only when one of assets is ETH or FLAG_ENABLE_MULTI_PATH_ETH uint256 internal constant FLAG_DISABLE_IDLE = 0x800000; uint256 internal constant FLAG_DISABLE_MOONISWAP = 0x1000000; uint256 internal constant FLAG_DISABLE_UNISWAP_V2 = 0x2000000; uint256 internal constant FLAG_DISABLE_UNISWAP_V2_ETH = 0x4000000; uint256 internal constant FLAG_DISABLE_UNISWAP_V2_DAI = 0x8000000; uint256 internal constant FLAG_DISABLE_UNISWAP_V2_USDC = 0x10000000; uint256 internal constant FLAG_DISABLE_ALL_SPLIT_SOURCES = 0x20000000; uint256 internal constant FLAG_DISABLE_ALL_WRAP_SOURCES = 0x40000000; uint256 internal constant FLAG_DISABLE_CURVE_PAX = 0x80000000; uint256 internal constant FLAG_DISABLE_CURVE_RENBTC = 0x100000000; uint256 internal constant FLAG_DISABLE_CURVE_TBTC = 0x200000000; uint256 internal constant DEPRECATED_FLAG_ENABLE_MULTI_PATH_USDT = 0x400000000; // Deprecated, Turned off by default uint256 internal constant DEPRECATED_FLAG_ENABLE_MULTI_PATH_WBTC = 0x800000000; // Deprecated, Turned off by default uint256 internal constant DEPRECATED_FLAG_ENABLE_MULTI_PATH_TBTC = 0x1000000000; // Deprecated, Turned off by default uint256 internal constant DEPRECATED_FLAG_ENABLE_MULTI_PATH_RENBTC = 0x2000000000; // Deprecated, Turned off by default uint256 internal constant FLAG_DISABLE_DFORCE_SWAP = 0x4000000000; uint256 internal constant FLAG_DISABLE_SHELL = 0x8000000000; uint256 internal constant FLAG_ENABLE_CHI_BURN = 0x10000000000; uint256 internal constant FLAG_DISABLE_MSTABLE_MUSD = 0x20000000000; uint256 internal constant FLAG_DISABLE_CURVE_SBTC = 0x40000000000; uint256 internal constant FLAG_DISABLE_DMM = 0x80000000000; uint256 internal constant FLAG_DISABLE_UNISWAP_ALL = 0x100000000000; uint256 internal constant FLAG_DISABLE_CURVE_ALL = 0x200000000000; uint256 internal constant FLAG_DISABLE_UNISWAP_V2_ALL = 0x400000000000; uint256 internal constant FLAG_DISABLE_SPLIT_RECALCULATION = 0x800000000000; uint256 internal constant FLAG_DISABLE_BALANCER_ALL = 0x1000000000000; uint256 internal constant FLAG_DISABLE_BALANCER_1 = 0x2000000000000; uint256 internal constant FLAG_DISABLE_BALANCER_2 = 0x4000000000000; uint256 internal constant FLAG_DISABLE_BALANCER_3 = 0x8000000000000; uint256 internal constant DEPRECATED_FLAG_ENABLE_KYBER_UNISWAP_RESERVE = 0x10000000000000; // Deprecated, Turned off by default uint256 internal constant DEPRECATED_FLAG_ENABLE_KYBER_OASIS_RESERVE = 0x20000000000000; // Deprecated, Turned off by default uint256 internal constant DEPRECATED_FLAG_ENABLE_KYBER_BANCOR_RESERVE = 0x40000000000000; // Deprecated, Turned off by default uint256 internal constant FLAG_ENABLE_REFERRAL_GAS_SPONSORSHIP = 0x80000000000000; // Turned off by default uint256 internal constant DEPRECATED_FLAG_ENABLE_MULTI_PATH_COMP = 0x100000000000000; // Deprecated, Turned off by default uint256 internal constant FLAG_DISABLE_KYBER_ALL = 0x200000000000000; uint256 internal constant FLAG_DISABLE_KYBER_1 = 0x400000000000000; uint256 internal constant FLAG_DISABLE_KYBER_2 = 0x800000000000000; uint256 internal constant FLAG_DISABLE_KYBER_3 = 0x1000000000000000; uint256 internal constant FLAG_DISABLE_KYBER_4 = 0x2000000000000000; uint256 internal constant FLAG_ENABLE_CHI_BURN_BY_ORIGIN = 0x4000000000000000; uint256 internal constant FLAG_DISABLE_MOONISWAP_ALL = 0x8000000000000000; uint256 internal constant FLAG_DISABLE_MOONISWAP_ETH = 0x10000000000000000; uint256 internal constant FLAG_DISABLE_MOONISWAP_DAI = 0x20000000000000000; uint256 internal constant FLAG_DISABLE_MOONISWAP_USDC = 0x40000000000000000; uint256 internal constant FLAG_DISABLE_MOONISWAP_POOL_TOKEN = 0x80000000000000000; } contract IOneSplit is IOneSplitConsts { function getExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags // See constants in IOneSplit.sol ) public view returns( uint256 returnAmount, uint256[] memory distribution ); function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, // See constants in IOneSplit.sol uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ); function swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 minReturn, uint256[] memory distribution, uint256 flags ) public payable returns(uint256 returnAmount); } contract IOneSplitMulti is IOneSplit { function getExpectedReturnWithGasMulti( IERC20[] memory tokens, uint256 amount, uint256[] memory parts, uint256[] memory flags, uint256[] memory destTokenEthPriceTimesGasPrices ) public view returns( uint256[] memory returnAmounts, uint256 estimateGasAmount, uint256[] memory distribution ); function swapMulti( IERC20[] memory tokens, uint256 amount, uint256 minReturn, uint256[] memory distribution, uint256[] memory flags ) public payable returns(uint256 returnAmount); } // File: @openzeppelin/contracts/math/SafeMath.sol pragma solidity ^0.5.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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 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. * * _Available since v2.4.0._ */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File: contracts/interface/IUniswapExchange.sol pragma solidity ^0.5.0; interface IUniswapExchange { function getEthToTokenInputPrice(uint256 ethSold) external view returns (uint256 tokensBought); function getTokenToEthInputPrice(uint256 tokensSold) external view returns (uint256 ethBought); function ethToTokenSwapInput(uint256 minTokens, uint256 deadline) external payable returns (uint256 tokensBought); function tokenToEthSwapInput(uint256 tokensSold, uint256 minEth, uint256 deadline) external returns (uint256 ethBought); function tokenToTokenSwapInput( uint256 tokensSold, uint256 minTokensBought, uint256 minEthBought, uint256 deadline, address tokenAddr ) external returns (uint256 tokensBought); } // File: contracts/interface/IUniswapFactory.sol pragma solidity ^0.5.0; interface IUniswapFactory { function getExchange(IERC20 token) external view returns (IUniswapExchange exchange); } // File: contracts/interface/IKyberNetworkContract.sol pragma solidity ^0.5.0; interface IKyberNetworkContract { function searchBestRate(IERC20 src, IERC20 dest, uint256 srcAmount, bool usePermissionless) external view returns (address reserve, uint256 rate); } // File: contracts/interface/IKyberNetworkProxy.sol pragma solidity ^0.5.0; interface IKyberNetworkProxy { function getExpectedRateAfterFee( IERC20 src, IERC20 dest, uint256 srcQty, uint256 platformFeeBps, bytes calldata hint ) external view returns (uint256 expectedRate); function tradeWithHintAndFee( IERC20 src, uint256 srcAmount, IERC20 dest, address payable destAddress, uint256 maxDestAmount, uint256 minConversionRate, address payable platformWallet, uint256 platformFeeBps, bytes calldata hint ) external payable returns (uint256 destAmount); function kyberNetworkContract() external view returns (IKyberNetworkContract); // TODO: Limit usage by tx.gasPrice // function maxGasPrice() external view returns (uint256); // TODO: Limit usage by user cap // function getUserCapInWei(address user) external view returns (uint256); // function getUserCapInTokenWei(address user, IERC20 token) external view returns (uint256); } // File: contracts/interface/IKyberStorage.sol pragma solidity ^0.5.0; interface IKyberStorage { function getReserveIdsPerTokenSrc( IERC20 token ) external view returns (bytes32[] memory); } // File: contracts/interface/IKyberHintHandler.sol pragma solidity ^0.5.0; interface IKyberHintHandler { enum TradeType { BestOfAll, MaskIn, MaskOut, Split } function buildTokenToEthHint( IERC20 tokenSrc, TradeType tokenToEthType, bytes32[] calldata tokenToEthReserveIds, uint256[] calldata tokenToEthSplits ) external view returns (bytes memory hint); function buildEthToTokenHint( IERC20 tokenDest, TradeType ethToTokenType, bytes32[] calldata ethToTokenReserveIds, uint256[] calldata ethToTokenSplits ) external view returns (bytes memory hint); } // File: contracts/interface/IBancorNetwork.sol pragma solidity ^0.5.0; interface IBancorNetwork { function getReturnByPath(address[] calldata path, uint256 amount) external view returns (uint256 returnAmount, uint256 conversionFee); function claimAndConvert(address[] calldata path, uint256 amount, uint256 minReturn) external returns (uint256); function convert(address[] calldata path, uint256 amount, uint256 minReturn) external payable returns (uint256); } // File: contracts/interface/IBancorContractRegistry.sol pragma solidity ^0.5.0; contract IBancorContractRegistry { function addressOf(bytes32 contractName) external view returns (address); } // File: contracts/interface/IBancorNetworkPathFinder.sol pragma solidity ^0.5.0; interface IBancorNetworkPathFinder { function generatePath(IERC20 sourceToken, IERC20 targetToken) external view returns (address[] memory); } // File: contracts/interface/IBancorConverterRegistry.sol pragma solidity ^0.5.0; interface IBancorConverterRegistry { function getConvertibleTokenSmartTokenCount(IERC20 convertibleToken) external view returns(uint256); function getConvertibleTokenSmartTokens(IERC20 convertibleToken) external view returns(address[] memory); function getConvertibleTokenSmartToken(IERC20 convertibleToken, uint256 index) external view returns(address); function isConvertibleTokenSmartToken(IERC20 convertibleToken, address value) external view returns(bool); } // File: contracts/interface/IBancorEtherToken.sol pragma solidity ^0.5.0; contract IBancorEtherToken is IERC20 { function deposit() external payable; function withdraw(uint256 amount) external; } // File: contracts/interface/IBancorFinder.sol pragma solidity ^0.5.0; interface IBancorFinder { function buildBancorPath( IERC20 fromToken, IERC20 destToken ) external view returns(address[] memory path); } // File: contracts/interface/IOasisExchange.sol pragma solidity ^0.5.0; interface IOasisExchange { function getBuyAmount(IERC20 buyGem, IERC20 payGem, uint256 payAmt) external view returns (uint256 fillAmt); function sellAllAmount(IERC20 payGem, uint256 payAmt, IERC20 buyGem, uint256 minFillAmount) external returns (uint256 fillAmt); } // File: contracts/interface/IWETH.sol pragma solidity ^0.5.0; contract IWETH is IERC20 { function deposit() external payable; function withdraw(uint256 amount) external; } // File: contracts/interface/ICurve.sol pragma solidity ^0.5.0; interface ICurve { // solium-disable-next-line mixedcase function get_dy_underlying(int128 i, int128 j, uint256 dx) external view returns(uint256 dy); // solium-disable-next-line mixedcase function get_dy(int128 i, int128 j, uint256 dx) external view returns(uint256 dy); // solium-disable-next-line mixedcase function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 minDy) external; // solium-disable-next-line mixedcase function exchange(int128 i, int128 j, uint256 dx, uint256 minDy) external; } contract ICurveRegistry { function get_pool_info(address pool) external view returns( uint256[8] memory balances, uint256[8] memory underlying_balances, uint256[8] memory decimals, uint256[8] memory underlying_decimals, address lp_token, uint256 A, uint256 fee ); } contract ICurveCalculator { function get_dy( int128 nCoins, uint256[8] calldata balances, uint256 amp, uint256 fee, uint256[8] calldata rates, uint256[8] calldata precisions, bool underlying, int128 i, int128 j, uint256[100] calldata dx ) external view returns(uint256[100] memory dy); } // File: contracts/interface/IChai.sol pragma solidity ^0.5.0; interface IPot { function dsr() external view returns (uint256); function chi() external view returns (uint256); function rho() external view returns (uint256); function drip() external returns (uint256); function join(uint256) external; function exit(uint256) external; } contract IChai is IERC20 { function POT() public view returns (IPot); function join(address dst, uint256 wad) external; function exit(address src, uint256 wad) external; } library ChaiHelper { IPot private constant POT = IPot(0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7); uint256 private constant RAY = 10**27; function _mul(uint256 x, uint256 y) private pure returns (uint256 z) { require(y == 0 || (z = x * y) / y == x); } function _rmul(uint256 x, uint256 y) private pure returns (uint256 z) { // always rounds down z = _mul(x, y) / RAY; } function _rdiv(uint256 x, uint256 y) private pure returns (uint256 z) { // always rounds down z = _mul(x, RAY) / y; } function rpow(uint256 x, uint256 n, uint256 base) private pure returns (uint256 z) { // solium-disable-next-line security/no-inline-assembly assembly { switch x case 0 { switch n case 0 { z := base } default { z := 0 } } default { switch mod(n, 2) case 0 { z := base } default { z := x } let half := div(base, 2) // for rounding. for { n := div(n, 2) } n { n := div(n, 2) } { let xx := mul(x, x) if iszero(eq(div(xx, x), x)) { revert(0, 0) } let xxRound := add(xx, half) if lt(xxRound, xx) { revert(0, 0) } x := div(xxRound, base) if mod(n, 2) { let zx := mul(z, x) if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0, 0) } let zxRound := add(zx, half) if lt(zxRound, zx) { revert(0, 0) } z := div(zxRound, base) } } } } } function potDrip() private view returns (uint256) { return _rmul(rpow(POT.dsr(), now - POT.rho(), RAY), POT.chi()); } function chaiPrice(IChai chai) internal view returns(uint256) { return chaiToDai(chai, 1e18); } function daiToChai( IChai /*chai*/, uint256 amount ) internal view returns (uint256) { uint256 chi = (now > POT.rho()) ? potDrip() : POT.chi(); return _rdiv(amount, chi); } function chaiToDai( IChai /*chai*/, uint256 amount ) internal view returns (uint256) { uint256 chi = (now > POT.rho()) ? potDrip() : POT.chi(); return _rmul(chi, amount); } } // File: contracts/interface/ICompound.sol pragma solidity ^0.5.0; contract ICompound { function markets(address cToken) external view returns (bool isListed, uint256 collateralFactorMantissa); } contract ICompoundToken is IERC20 { function underlying() external view returns (address); function exchangeRateStored() external view returns (uint256); function mint(uint256 mintAmount) external returns (uint256); function redeem(uint256 redeemTokens) external returns (uint256); } contract ICompoundEther is IERC20 { function mint() external payable; function redeem(uint256 redeemTokens) external returns (uint256); } // File: contracts/interface/ICompoundRegistry.sol pragma solidity ^0.5.0; contract ICompoundRegistry { function tokenByCToken(ICompoundToken cToken) external view returns(IERC20); function cTokenByToken(IERC20 token) external view returns(ICompoundToken); } // File: contracts/interface/IAaveToken.sol pragma solidity ^0.5.0; contract IAaveToken is IERC20 { function underlyingAssetAddress() external view returns (IERC20); function redeem(uint256 amount) external; } interface IAaveLendingPool { function core() external view returns (address); function deposit(IERC20 token, uint256 amount, uint16 refCode) external payable; } // File: contracts/interface/IAaveRegistry.sol pragma solidity ^0.5.0; contract IAaveRegistry { function tokenByAToken(IAaveToken aToken) external view returns(IERC20); function aTokenByToken(IERC20 token) external view returns(IAaveToken); } // File: contracts/interface/IMooniswap.sol pragma solidity ^0.5.0; interface IMooniswapRegistry { function pools(IERC20 token1, IERC20 token2) external view returns(IMooniswap); function isPool(address addr) external view returns(bool); } interface IMooniswap { function fee() external view returns (uint256); function tokens(uint256 i) external view returns (IERC20); function deposit(uint256[] calldata amounts, uint256 minReturn) external payable returns(uint256 fairSupply); function withdraw(uint256 amount, uint256[] calldata minReturns) external; function getBalanceForAddition(IERC20 token) external view returns(uint256); function getBalanceForRemoval(IERC20 token) external view returns(uint256); function getReturn( IERC20 fromToken, IERC20 destToken, uint256 amount ) external view returns(uint256 returnAmount); function swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 minReturn, address referral ) external payable returns(uint256 returnAmount); } // File: @openzeppelin/contracts/utils/Address.sol pragma solidity ^0.5.5; /** * @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) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @dev Converts an `address` into `address payable`. Note that this is * simply a type cast: the actual underlying value is not changed. * * _Available since v2.4.0._ */ function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } /** * @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]. * * _Available since v2.4.0._ */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-call-value (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } } // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol pragma solidity ^0.5.0; /** * @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 ERC20;` 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)); } 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. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length require(address(token).isContract(), "SafeERC20: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, "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"); } } } // File: contracts/UniversalERC20.sol pragma solidity ^0.5.0; library UniversalERC20 { using SafeMath for uint256; using SafeERC20 for IERC20; IERC20 private constant ZERO_ADDRESS = IERC20(0x0000000000000000000000000000000000000000); IERC20 private constant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); function universalTransfer(IERC20 token, address to, uint256 amount) internal returns(bool) { if (amount == 0) { return true; } if (isETH(token)) { address(uint160(to)).transfer(amount); } else { token.safeTransfer(to, amount); return true; } } function universalTransferFrom(IERC20 token, address from, address to, uint256 amount) internal { if (amount == 0) { return; } if (isETH(token)) { require(from == msg.sender && msg.value >= amount, "Wrong useage of ETH.universalTransferFrom()"); if (to != address(this)) { address(uint160(to)).transfer(amount); } if (msg.value > amount) { msg.sender.transfer(msg.value.sub(amount)); } } else { token.safeTransferFrom(from, to, amount); } } function universalTransferFromSenderToThis(IERC20 token, uint256 amount) internal { if (amount == 0) { return; } if (isETH(token)) { if (msg.value > amount) { // Return remainder if exist msg.sender.transfer(msg.value.sub(amount)); } } else { token.safeTransferFrom(msg.sender, address(this), amount); } } function universalApprove(IERC20 token, address to, uint256 amount) internal { if (!isETH(token)) { if (amount == 0) { token.safeApprove(to, 0); return; } uint256 allowance = token.allowance(address(this), to); if (allowance < amount) { if (allowance > 0) { token.safeApprove(to, 0); } token.safeApprove(to, amount); } } } function universalBalanceOf(IERC20 token, address who) internal view returns (uint256) { if (isETH(token)) { return who.balance; } else { return token.balanceOf(who); } } function universalDecimals(IERC20 token) internal view returns (uint256) { if (isETH(token)) { return 18; } (bool success, bytes memory data) = address(token).staticcall.gas(10000)( abi.encodeWithSignature("decimals()") ); if (!success || data.length == 0) { (success, data) = address(token).staticcall.gas(10000)( abi.encodeWithSignature("DECIMALS()") ); } return (success && data.length > 0) ? abi.decode(data, (uint256)) : 18; } function isETH(IERC20 token) internal pure returns(bool) { return (address(token) == address(ZERO_ADDRESS) || address(token) == address(ETH_ADDRESS)); } function eq(IERC20 a, IERC20 b) internal pure returns(bool) { return a == b || (isETH(a) && isETH(b)); } function notExist(IERC20 token) internal pure returns(bool) { return (address(token) == address(-1)); } } // File: contracts/interface/IUniswapV2Exchange.sol pragma solidity ^0.5.0; interface IUniswapV2Exchange { function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; } library UniswapV2ExchangeLib { using SafeMath for uint256; using UniversalERC20 for IERC20; function getReturn( IUniswapV2Exchange exchange, IERC20 fromToken, IERC20 destToken, uint amountIn ) internal view returns (uint256) { uint256 reserveIn = fromToken.universalBalanceOf(address(exchange)); uint256 reserveOut = destToken.universalBalanceOf(address(exchange)); uint256 amountInWithFee = amountIn.mul(997); uint256 numerator = amountInWithFee.mul(reserveOut); uint256 denominator = reserveIn.mul(1000).add(amountInWithFee); return (denominator == 0) ? 0 : numerator.div(denominator); } } // File: contracts/interface/IUniswapV2Factory.sol pragma solidity ^0.5.0; interface IUniswapV2Factory { function getPair(IERC20 tokenA, IERC20 tokenB) external view returns (IUniswapV2Exchange pair); } // File: contracts/interface/IDForceSwap.sol pragma solidity ^0.5.0; interface IDForceSwap { function getAmountByInput(IERC20 input, IERC20 output, uint256 amount) external view returns(uint256); function swap(IERC20 input, IERC20 output, uint256 amount) external; } // File: contracts/interface/IShell.sol pragma solidity ^0.5.0; interface IShell { function viewOriginTrade( address origin, address target, uint256 originAmount ) external view returns (uint256); function swapByOrigin( address origin, address target, uint256 originAmount, uint256 minTargetAmount, uint256 deadline ) external returns (uint256); } // File: contracts/interface/IMStable.sol pragma solidity ^0.5.0; contract IMStable is IERC20 { function getSwapOutput( IERC20 _input, IERC20 _output, uint256 _quantity ) external view returns (bool, string memory, uint256 output); function swap( IERC20 _input, IERC20 _output, uint256 _quantity, address _recipient ) external returns (uint256 output); function redeem( IERC20 _basset, uint256 _bassetQuantity ) external returns (uint256 massetRedeemed); } interface IMassetValidationHelper { /** * @dev Returns a valid bAsset to redeem * @param _mAsset Masset addr * @return valid bool * @return string message * @return address of bAsset to redeem */ function suggestRedeemAsset( IERC20 _mAsset ) external view returns ( bool valid, string memory err, address token ); /** * @dev Returns a valid bAsset with which to mint * @param _mAsset Masset addr * @return valid bool * @return string message * @return address of bAsset to mint */ function suggestMintAsset( IERC20 _mAsset ) external view returns ( bool valid, string memory err, address token ); /** * @dev Determines if a given Redemption is valid * @param _mAsset Address of the given mAsset (e.g. mUSD) * @param _mAssetQuantity Amount of mAsset to redeem (in mUSD units) * @param _outputBasset Desired output bAsset * @return valid * @return validity reason * @return output in bAsset units * @return bAssetQuantityArg - required input argument to the 'redeem' call */ function getRedeemValidity( IERC20 _mAsset, uint256 _mAssetQuantity, IERC20 _outputBasset ) external view returns ( bool valid, string memory, uint256 output, uint256 bassetQuantityArg ); } // File: contracts/interface/IBalancerPool.sol pragma solidity ^0.5.0; interface IBalancerPool { function getSwapFee() external view returns (uint256 balance); function getDenormalizedWeight(IERC20 token) external view returns (uint256 balance); function getBalance(IERC20 token) external view returns (uint256 balance); function swapExactAmountIn( IERC20 tokenIn, uint256 tokenAmountIn, IERC20 tokenOut, uint256 minAmountOut, uint256 maxPrice ) external returns (uint256 tokenAmountOut, uint256 spotPriceAfter); } // 0xA961672E8Db773be387e775bc4937C678F3ddF9a interface IBalancerHelper { function getReturns( IBalancerPool pool, IERC20 fromToken, IERC20 destToken, uint256[] calldata amounts ) external view returns(uint256[] memory rets); } // File: contracts/interface/IBalancerRegistry.sol pragma solidity ^0.5.0; interface IBalancerRegistry { event PoolAdded( address indexed pool ); event PoolTokenPairAdded( address indexed pool, address indexed fromToken, address indexed destToken ); event IndicesUpdated( address indexed fromToken, address indexed destToken, bytes32 oldIndices, bytes32 newIndices ); // Get info about pool pair for 1 SLOAD function getPairInfo(address pool, address fromToken, address destToken) external view returns(uint256 weight1, uint256 weight2, uint256 swapFee); // Pools function checkAddedPools(address pool) external view returns(bool); function getAddedPoolsLength() external view returns(uint256); function getAddedPools() external view returns(address[] memory); function getAddedPoolsWithLimit(uint256 offset, uint256 limit) external view returns(address[] memory result); // Tokens function getAllTokensLength() external view returns(uint256); function getAllTokens() external view returns(address[] memory); function getAllTokensWithLimit(uint256 offset, uint256 limit) external view returns(address[] memory result); // Pairs function getPoolsLength(address fromToken, address destToken) external view returns(uint256); function getPools(address fromToken, address destToken) external view returns(address[] memory); function getPoolsWithLimit(address fromToken, address destToken, uint256 offset, uint256 limit) external view returns(address[] memory result); function getBestPools(address fromToken, address destToken) external view returns(address[] memory pools); function getBestPoolsWithLimit(address fromToken, address destToken, uint256 limit) external view returns(address[] memory pools); // Get swap rates function getPoolReturn(address pool, address fromToken, address destToken, uint256 amount) external view returns(uint256); function getPoolReturns(address pool, address fromToken, address destToken, uint256[] calldata amounts) external view returns(uint256[] memory result); // Add and update registry function addPool(address pool) external returns(uint256 listed); function addPools(address[] calldata pools) external returns(uint256[] memory listed); function updatedIndices(address[] calldata tokens, uint256 lengthLimit) external; } // File: contracts/BalancerLib.sol pragma solidity ^0.5.0; library BalancerLib { uint public constant BONE = 10**18; uint public constant MIN_BOUND_TOKENS = 2; uint public constant MAX_BOUND_TOKENS = 8; uint public constant MIN_FEE = BONE / 10**6; uint public constant MAX_FEE = BONE / 10; uint public constant EXIT_FEE = 0; uint public constant MIN_WEIGHT = BONE; uint public constant MAX_WEIGHT = BONE * 50; uint public constant MAX_TOTAL_WEIGHT = BONE * 50; uint public constant MIN_BALANCE = BONE / 10**12; uint public constant INIT_POOL_SUPPLY = BONE * 100; uint public constant MIN_BPOW_BASE = 1 wei; uint public constant MAX_BPOW_BASE = (2 * BONE) - 1 wei; uint public constant BPOW_PRECISION = BONE / 10**10; uint public constant MAX_IN_RATIO = BONE / 2; uint public constant MAX_OUT_RATIO = (BONE / 3) + 1 wei; function btoi(uint a) internal pure returns (uint) { return a / BONE; } function bfloor(uint a) internal pure returns (uint) { return btoi(a) * BONE; } function badd(uint a, uint b) internal pure returns (uint) { uint c = a + b; require(c >= a, "ERR_ADD_OVERFLOW"); return c; } function bsub(uint a, uint b) internal pure returns (uint) { (uint c, bool flag) = bsubSign(a, b); require(!flag, "ERR_SUB_UNDERFLOW"); return c; } function bsubSign(uint a, uint b) internal pure returns (uint, bool) { if (a >= b) { return (a - b, false); } else { return (b - a, true); } } function bmul(uint a, uint b) internal pure returns (uint) { uint c0 = a * b; require(a == 0 || c0 / a == b, "ERR_MUL_OVERFLOW"); uint c1 = c0 + (BONE / 2); require(c1 >= c0, "ERR_MUL_OVERFLOW"); uint c2 = c1 / BONE; return c2; } function bdiv(uint a, uint b) internal pure returns (uint) { require(b != 0, "ERR_DIV_ZERO"); uint c0 = a * BONE; require(a == 0 || c0 / a == BONE, "ERR_DIV_INTERNAL"); // bmul overflow uint c1 = c0 + (b / 2); require(c1 >= c0, "ERR_DIV_INTERNAL"); // badd require uint c2 = c1 / b; return c2; } // DSMath.wpow function bpowi(uint a, uint n) internal pure returns (uint) { uint z = n % 2 != 0 ? a : BONE; for (n /= 2; n != 0; n /= 2) { a = bmul(a, a); if (n % 2 != 0) { z = bmul(z, a); } } return z; } // Compute b^(e.w) by splitting it into (b^e)*(b^0.w). // Use `bpowi` for `b^e` and `bpowK` for k iterations // of approximation of b^0.w function bpow(uint base, uint exp) internal pure returns (uint) { require(base >= MIN_BPOW_BASE, "ERR_BPOW_BASE_TOO_LOW"); require(base <= MAX_BPOW_BASE, "ERR_BPOW_BASE_TOO_HIGH"); uint whole = bfloor(exp); uint remain = bsub(exp, whole); uint wholePow = bpowi(base, btoi(whole)); if (remain == 0) { return wholePow; } uint partialResult = bpowApprox(base, remain, BPOW_PRECISION); return bmul(wholePow, partialResult); } function bpowApprox(uint base, uint exp, uint precision) internal pure returns (uint) { // term 0: uint a = exp; (uint x, bool xneg) = bsubSign(base, BONE); uint term = BONE; uint sum = term; bool negative = false; // term(k) = numer / denom // = (product(a - i - 1, i=1-->k) * x^k) / (k!) // each iteration, multiply previous term by (a-(k-1)) * x / k // continue until term is less than precision for (uint i = 1; term >= precision; i++) { uint bigK = i * BONE; (uint c, bool cneg) = bsubSign(a, bsub(bigK, BONE)); term = bmul(term, bmul(c, x)); term = bdiv(term, bigK); if (term == 0) break; if (xneg) negative = !negative; if (cneg) negative = !negative; if (negative) { sum = bsub(sum, term); } else { sum = badd(sum, term); } } return sum; } /********************************************************************************************** // calcSpotPrice // // sP = spotPrice // // bI = tokenBalanceIn ( bI / wI ) 1 // // bO = tokenBalanceOut sP = ----------- * ---------- // // wI = tokenWeightIn ( bO / wO ) ( 1 - sF ) // // wO = tokenWeightOut // // sF = swapFee // **********************************************************************************************/ function calcSpotPrice( uint tokenBalanceIn, uint tokenWeightIn, uint tokenBalanceOut, uint tokenWeightOut, uint swapFee ) internal pure returns (uint spotPrice) { uint numer = bdiv(tokenBalanceIn, tokenWeightIn); uint denom = bdiv(tokenBalanceOut, tokenWeightOut); uint ratio = bdiv(numer, denom); uint scale = bdiv(BONE, bsub(BONE, swapFee)); return (spotPrice = bmul(ratio, scale)); } /********************************************************************************************** // calcOutGivenIn // // aO = tokenAmountOut // // bO = tokenBalanceOut // // bI = tokenBalanceIn / / bI \ (wI / wO) \ // // aI = tokenAmountIn aO = bO * | 1 - | -------------------------- | ^ | // // wI = tokenWeightIn \ \ ( bI + ( aI * ( 1 - sF )) / / // // wO = tokenWeightOut // // sF = swapFee // **********************************************************************************************/ function calcOutGivenIn( uint tokenBalanceIn, uint tokenWeightIn, uint tokenBalanceOut, uint tokenWeightOut, uint tokenAmountIn, uint swapFee ) internal pure returns (uint tokenAmountOut) { uint weightRatio = bdiv(tokenWeightIn, tokenWeightOut); uint adjustedIn = bsub(BONE, swapFee); adjustedIn = bmul(tokenAmountIn, adjustedIn); uint y = bdiv(tokenBalanceIn, badd(tokenBalanceIn, adjustedIn)); if (y == 0) { return 0; } uint foo = bpow(y, weightRatio); uint bar = bsub(BONE, foo); tokenAmountOut = bmul(tokenBalanceOut, bar); return tokenAmountOut; } /********************************************************************************************** // calcInGivenOut // // aI = tokenAmountIn // // bO = tokenBalanceOut / / bO \ (wO / wI) \ // // bI = tokenBalanceIn bI * | | ------------ | ^ - 1 | // // aO = tokenAmountOut aI = \ \ ( bO - aO ) / / // // wI = tokenWeightIn -------------------------------------------- // // wO = tokenWeightOut ( 1 - sF ) // // sF = swapFee // **********************************************************************************************/ function calcInGivenOut( uint tokenBalanceIn, uint tokenWeightIn, uint tokenBalanceOut, uint tokenWeightOut, uint tokenAmountOut, uint swapFee ) internal pure returns (uint tokenAmountIn) { uint weightRatio = bdiv(tokenWeightOut, tokenWeightIn); uint diff = bsub(tokenBalanceOut, tokenAmountOut); uint y = bdiv(tokenBalanceOut, diff); if (y == 0) { return 0; } uint foo = bpow(y, weightRatio); foo = bsub(foo, BONE); tokenAmountIn = bsub(BONE, swapFee); tokenAmountIn = bdiv(bmul(tokenBalanceIn, foo), tokenAmountIn); return tokenAmountIn; } /********************************************************************************************** // calcPoolOutGivenSingleIn // // pAo = poolAmountOut / \ // // tAi = tokenAmountIn /// / // wI \ \\ \ wI \ // // wI = tokenWeightIn //| tAi *| 1 - || 1 - -- | * sF || + tBi \ -- \ // // tW = totalWeight pAo=|| \ \ \\ tW / // | ^ tW | * pS - pS // // tBi = tokenBalanceIn \\ ------------------------------------- / / // // pS = poolSupply \\ tBi / / // // sF = swapFee \ / // **********************************************************************************************/ function calcPoolOutGivenSingleIn( uint tokenBalanceIn, uint tokenWeightIn, uint poolSupply, uint totalWeight, uint tokenAmountIn, uint swapFee ) internal pure returns (uint poolAmountOut) { // Charge the trading fee for the proportion of tokenAi /// which is implicitly traded to the other pool tokens. // That proportion is (1- weightTokenIn) // tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee); uint normalizedWeight = bdiv(tokenWeightIn, totalWeight); uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); uint tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz)); uint newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee); uint tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn); // uint newPoolSupply = (ratioTi ^ weightTi) * poolSupply; uint poolRatio = bpow(tokenInRatio, normalizedWeight); uint newPoolSupply = bmul(poolRatio, poolSupply); poolAmountOut = bsub(newPoolSupply, poolSupply); return poolAmountOut; } /********************************************************************************************** // calcSingleInGivenPoolOut // // tAi = tokenAmountIn //(pS + pAo)\ / 1 \\ // // pS = poolSupply || --------- | ^ | --------- || * bI - bI // // pAo = poolAmountOut \\ pS / \(wI / tW)// // // bI = balanceIn tAi = -------------------------------------------- // // wI = weightIn / wI \ // // tW = totalWeight | 1 - ---- | * sF // // sF = swapFee \ tW / // **********************************************************************************************/ function calcSingleInGivenPoolOut( uint tokenBalanceIn, uint tokenWeightIn, uint poolSupply, uint totalWeight, uint poolAmountOut, uint swapFee ) internal pure returns (uint tokenAmountIn) { uint normalizedWeight = bdiv(tokenWeightIn, totalWeight); uint newPoolSupply = badd(poolSupply, poolAmountOut); uint poolRatio = bdiv(newPoolSupply, poolSupply); //uint newBalTi = poolRatio^(1/weightTi) * balTi; uint boo = bdiv(BONE, normalizedWeight); uint tokenInRatio = bpow(poolRatio, boo); uint newTokenBalanceIn = bmul(tokenInRatio, tokenBalanceIn); uint tokenAmountInAfterFee = bsub(newTokenBalanceIn, tokenBalanceIn); // Do reverse order of fees charged in joinswap_ExternAmountIn, this way // ``` pAo == joinswap_ExternAmountIn(Ti, joinswap_PoolAmountOut(pAo, Ti)) ``` //uint tAi = tAiAfterFee / (1 - (1-weightTi) * swapFee) ; uint zar = bmul(bsub(BONE, normalizedWeight), swapFee); tokenAmountIn = bdiv(tokenAmountInAfterFee, bsub(BONE, zar)); return tokenAmountIn; } /********************************************************************************************** // calcSingleOutGivenPoolIn // // tAo = tokenAmountOut / / \\ // // bO = tokenBalanceOut / // pS - (pAi * (1 - eF)) \ / 1 \ \\ // // pAi = poolAmountIn | bO - || ----------------------- | ^ | --------- | * b0 || // // ps = poolSupply \ \\ pS / \(wO / tW)/ // // // wI = tokenWeightIn tAo = \ \ // // // tW = totalWeight / / wO \ \ // // sF = swapFee * | 1 - | 1 - ---- | * sF | // // eF = exitFee \ \ tW / / // **********************************************************************************************/ function calcSingleOutGivenPoolIn( uint tokenBalanceOut, uint tokenWeightOut, uint poolSupply, uint totalWeight, uint poolAmountIn, uint swapFee ) internal pure returns (uint tokenAmountOut) { uint normalizedWeight = bdiv(tokenWeightOut, totalWeight); // charge exit fee on the pool token side // pAiAfterExitFee = pAi*(1-exitFee) uint poolAmountInAfterExitFee = bmul(poolAmountIn, bsub(BONE, EXIT_FEE)); uint newPoolSupply = bsub(poolSupply, poolAmountInAfterExitFee); uint poolRatio = bdiv(newPoolSupply, poolSupply); // newBalTo = poolRatio^(1/weightTo) * balTo; uint tokenOutRatio = bpow(poolRatio, bdiv(BONE, normalizedWeight)); uint newTokenBalanceOut = bmul(tokenOutRatio, tokenBalanceOut); uint tokenAmountOutBeforeSwapFee = bsub(tokenBalanceOut, newTokenBalanceOut); // charge swap fee on the output token side //uint tAo = tAoBeforeSwapFee * (1 - (1-weightTo) * swapFee) uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); tokenAmountOut = bmul(tokenAmountOutBeforeSwapFee, bsub(BONE, zaz)); return tokenAmountOut; } /********************************************************************************************** // calcPoolInGivenSingleOut // // pAi = poolAmountIn // / tAo \\ / wO \ \ // // bO = tokenBalanceOut // | bO - -------------------------- |\ | ---- | \ // // tAo = tokenAmountOut pS - || \ 1 - ((1 - (tO / tW)) * sF)/ | ^ \ tW / * pS | // // ps = poolSupply \\ -----------------------------------/ / // // wO = tokenWeightOut pAi = \\ bO / / // // tW = totalWeight ------------------------------------------------------------- // // sF = swapFee ( 1 - eF ) // // eF = exitFee // **********************************************************************************************/ function calcPoolInGivenSingleOut( uint tokenBalanceOut, uint tokenWeightOut, uint poolSupply, uint totalWeight, uint tokenAmountOut, uint swapFee ) internal pure returns (uint poolAmountIn) { // charge swap fee on the output token side uint normalizedWeight = bdiv(tokenWeightOut, totalWeight); //uint tAoBeforeSwapFee = tAo / (1 - (1-weightTo) * swapFee) ; uint zoo = bsub(BONE, normalizedWeight); uint zar = bmul(zoo, swapFee); uint tokenAmountOutBeforeSwapFee = bdiv(tokenAmountOut, bsub(BONE, zar)); uint newTokenBalanceOut = bsub(tokenBalanceOut, tokenAmountOutBeforeSwapFee); uint tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut); //uint newPoolSupply = (ratioTo ^ weightTo) * poolSupply; uint poolRatio = bpow(tokenOutRatio, normalizedWeight); uint newPoolSupply = bmul(poolRatio, poolSupply); uint poolAmountInAfterExitFee = bsub(poolSupply, newPoolSupply); // charge exit fee on the pool token side // pAi = pAiAfterExitFee/(1-exitFee) poolAmountIn = bdiv(poolAmountInAfterExitFee, bsub(BONE, EXIT_FEE)); return poolAmountIn; } } // File: contracts/OneSplitBase.sol pragma solidity ^0.5.0; contract IOneSplitView is IOneSplitConsts { function getExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) public view returns( uint256 returnAmount, uint256[] memory distribution ); function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ); } library DisableFlags { function check(uint256 flags, uint256 flag) internal pure returns(bool) { return (flags & flag) != 0; } } contract OneSplitRoot is IOneSplitView { using SafeMath for uint256; using DisableFlags for uint256; using UniversalERC20 for IERC20; using UniversalERC20 for IWETH; using UniswapV2ExchangeLib for IUniswapV2Exchange; using ChaiHelper for IChai; uint256 constant internal DEXES_COUNT = 34; IERC20 constant internal ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); IERC20 constant internal ZERO_ADDRESS = IERC20(0); IBancorEtherToken constant internal bancorEtherToken = IBancorEtherToken(0xc0829421C1d260BD3cB3E0F06cfE2D52db2cE315); IWETH constant internal weth = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IChai constant internal chai = IChai(0x06AF07097C9Eeb7fD685c692751D5C66dB49c215); IERC20 constant internal dai = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); IERC20 constant internal usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); IERC20 constant internal usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); IERC20 constant internal tusd = IERC20(0x0000000000085d4780B73119b644AE5ecd22b376); IERC20 constant internal busd = IERC20(0x4Fabb145d64652a948d72533023f6E7A623C7C53); IERC20 constant internal susd = IERC20(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); IERC20 constant internal pax = IERC20(0x8E870D67F660D95d5be530380D0eC0bd388289E1); IERC20 constant internal renbtc = IERC20(0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D); IERC20 constant internal wbtc = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); IERC20 constant internal tbtc = IERC20(0x1bBE271d15Bb64dF0bc6CD28Df9Ff322F2eBD847); IERC20 constant internal hbtc = IERC20(0x0316EB71485b0Ab14103307bf65a021042c6d380); IERC20 constant internal sbtc = IERC20(0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6); IKyberNetworkProxy constant internal kyberNetworkProxy = IKyberNetworkProxy(0x9AAb3f75489902f3a48495025729a0AF77d4b11e); IKyberStorage constant internal kyberStorage = IKyberStorage(0xC8fb12402cB16970F3C5F4b48Ff68Eb9D1289301); IKyberHintHandler constant internal kyberHintHandler = IKyberHintHandler(0xa1C0Fa73c39CFBcC11ec9Eb1Afc665aba9996E2C); IUniswapFactory constant internal uniswapFactory = IUniswapFactory(0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95); IBancorContractRegistry constant internal bancorContractRegistry = IBancorContractRegistry(0x52Ae12ABe5D8BD778BD5397F99cA900624CfADD4); IBancorNetworkPathFinder constant internal bancorNetworkPathFinder = IBancorNetworkPathFinder(0x6F0cD8C4f6F06eAB664C7E3031909452b4B72861); //IBancorConverterRegistry constant internal bancorConverterRegistry = IBancorConverterRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); IBancorFinder constant internal bancorFinder = IBancorFinder(0x2B344e14dc2641D11D338C053C908c7A7D4c30B9); IOasisExchange constant internal oasisExchange = IOasisExchange(0x794e6e91555438aFc3ccF1c5076A74F42133d08D); ICurve constant internal curveCompound = ICurve(0xA2B47E3D5c44877cca798226B7B8118F9BFb7A56); ICurve constant internal curveUSDT = ICurve(0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C); ICurve constant internal curveY = ICurve(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); ICurve constant internal curveBinance = ICurve(0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27); ICurve constant internal curveSynthetix = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); ICurve constant internal curvePAX = ICurve(0x06364f10B501e868329afBc005b3492902d6C763); ICurve constant internal curveRenBTC = ICurve(0x93054188d876f558f4a66B2EF1d97d16eDf0895B); ICurve constant internal curveTBTC = ICurve(0x9726e9314eF1b96E45f40056bEd61A088897313E); ICurve constant internal curveSBTC = ICurve(0x7fC77b5c7614E1533320Ea6DDc2Eb61fa00A9714); IShell constant internal shell = IShell(0xA8253a440Be331dC4a7395B73948cCa6F19Dc97D); IAaveLendingPool constant internal aave = IAaveLendingPool(0x398eC7346DcD622eDc5ae82352F02bE94C62d119); ICompound constant internal compound = ICompound(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B); ICompoundEther constant internal cETH = ICompoundEther(0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5); IMooniswapRegistry constant internal mooniswapRegistry = IMooniswapRegistry(0xc12A7e093832E2d2267df225BAca60bD2B74C65F); IUniswapV2Factory constant internal uniswapV2 = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); IDForceSwap constant internal dforceSwap = IDForceSwap(0x03eF3f37856bD08eb47E2dE7ABc4Ddd2c19B60F2); IMStable constant internal musd = IMStable(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5); IMassetValidationHelper constant internal musd_helper = IMassetValidationHelper(0xaBcC93c3be238884cc3309C19Afd128fAfC16911); IBalancerRegistry constant internal balancerRegistry = IBalancerRegistry(0x65e67cbc342712DF67494ACEfc06fe951EE93982); ICurveCalculator constant internal curveCalculator = ICurveCalculator(0xc1DB00a8E5Ef7bfa476395cdbcc98235477cDE4E); ICurveRegistry constant internal curveRegistry = ICurveRegistry(0x7002B727Ef8F5571Cb5F9D70D13DBEEb4dFAe9d1); ICompoundRegistry constant internal compoundRegistry = ICompoundRegistry(0xF451Dbd7Ba14BFa7B1B78A766D3Ed438F79EE1D1); IAaveRegistry constant internal aaveRegistry = IAaveRegistry(0xEd8b133B7B88366E01Bb9E38305Ab11c26521494); IBalancerHelper constant internal balancerHelper = IBalancerHelper(0xA961672E8Db773be387e775bc4937C678F3ddF9a); int256 internal constant VERY_NEGATIVE_VALUE = -1e72; function _findBestDistribution( uint256 s, // parts int256[][] memory amounts // exchangesReturns ) internal pure returns( int256 returnAmount, uint256[] memory distribution ) { uint256 n = amounts.length; int256[][] memory answer = new int256[][](n); // int[n][s+1] uint256[][] memory parent = new uint256[][](n); // int[n][s+1] for (uint i = 0; i < n; i++) { answer[i] = new int256[](s + 1); parent[i] = new uint256[](s + 1); } for (uint j = 0; j <= s; j++) { answer[0][j] = amounts[0][j]; for (uint i = 1; i < n; i++) { answer[i][j] = -1e72; } parent[0][j] = 0; } for (uint i = 1; i < n; i++) { for (uint j = 0; j <= s; j++) { answer[i][j] = answer[i - 1][j]; parent[i][j] = j; for (uint k = 1; k <= j; k++) { if (answer[i - 1][j - k] + amounts[i][k] > answer[i][j]) { answer[i][j] = answer[i - 1][j - k] + amounts[i][k]; parent[i][j] = j - k; } } } } distribution = new uint256[](DEXES_COUNT); uint256 partsLeft = s; for (uint curExchange = n - 1; partsLeft > 0; curExchange--) { distribution[curExchange] = partsLeft - parent[curExchange][partsLeft]; partsLeft = parent[curExchange][partsLeft]; } returnAmount = (answer[n - 1][s] == VERY_NEGATIVE_VALUE) ? 0 : answer[n - 1][s]; } function _kyberReserveIdByTokens( IERC20 fromToken, IERC20 destToken ) internal view returns(bytes32) { if (!fromToken.isETH() && !destToken.isETH()) { return 0; } bytes32[] memory reserveIds = kyberStorage.getReserveIdsPerTokenSrc( fromToken.isETH() ? destToken : fromToken ); for (uint i = 0; i < reserveIds.length; i++) { if ((uint256(reserveIds[i]) >> 248) != 0xBB && // Bridge reserveIds[i] != 0xff4b796265722046707200000000000000000000000000000000000000000000 && // Reserve 1 reserveIds[i] != 0xffabcd0000000000000000000000000000000000000000000000000000000000 && // Reserve 2 reserveIds[i] != 0xff4f6e65426974205175616e7400000000000000000000000000000000000000) // Reserve 3 { return reserveIds[i]; } } return 0; } function _scaleDestTokenEthPriceTimesGasPrice( IERC20 fromToken, IERC20 destToken, uint256 destTokenEthPriceTimesGasPrice ) internal view returns(uint256) { if (fromToken == destToken) { return destTokenEthPriceTimesGasPrice; } uint256 mul = _cheapGetPrice(ETH_ADDRESS, destToken, 0.01 ether); uint256 div = _cheapGetPrice(ETH_ADDRESS, fromToken, 0.01 ether); if (div > 0) { return destTokenEthPriceTimesGasPrice.mul(mul).div(div); } return 0; } function _cheapGetPrice( IERC20 fromToken, IERC20 destToken, uint256 amount ) internal view returns(uint256 returnAmount) { (returnAmount,,) = this.getExpectedReturnWithGas( fromToken, destToken, amount, 1, FLAG_DISABLE_SPLIT_RECALCULATION | FLAG_DISABLE_ALL_SPLIT_SOURCES | FLAG_DISABLE_UNISWAP_V2_ALL | FLAG_DISABLE_UNISWAP, 0 ); } function _linearInterpolation( uint256 value, uint256 parts ) internal pure returns(uint256[] memory rets) { rets = new uint256[](parts); for (uint i = 0; i < parts; i++) { rets[i] = value.mul(i + 1).div(parts); } } function _tokensEqual(IERC20 tokenA, IERC20 tokenB) internal pure returns(bool) { return ((tokenA.isETH() && tokenB.isETH()) || tokenA == tokenB); } } contract OneSplitViewWrapBase is IOneSplitView, OneSplitRoot { function getExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags // See constants in IOneSplit.sol ) public view returns( uint256 returnAmount, uint256[] memory distribution ) { (returnAmount, , distribution) = this.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, 0 ); } function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return _getExpectedReturnRespectingGasFloor( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function _getExpectedReturnRespectingGasFloor( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, // See constants in IOneSplit.sol uint256 destTokenEthPriceTimesGasPrice ) internal view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ); } contract OneSplitView is IOneSplitView, OneSplitRoot { function getExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags // See constants in IOneSplit.sol ) public view returns( uint256 returnAmount, uint256[] memory distribution ) { (returnAmount, , distribution) = getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, 0 ); } function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, // See constants in IOneSplit.sol uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { distribution = new uint256[](DEXES_COUNT); if (fromToken == destToken) { return (amount, 0, distribution); } function(IERC20,IERC20,uint256,uint256,uint256) view returns(uint256[] memory, uint256)[DEXES_COUNT] memory reserves = _getAllReserves(flags); int256[][] memory matrix = new int256[][](DEXES_COUNT); uint256[DEXES_COUNT] memory gases; bool atLeastOnePositive = false; for (uint i = 0; i < DEXES_COUNT; i++) { uint256[] memory rets; (rets, gases[i]) = reserves[i](fromToken, destToken, amount, parts, flags); // Prepend zero and sub gas int256 gas = int256(gases[i].mul(destTokenEthPriceTimesGasPrice).div(1e18)); matrix[i] = new int256[](parts + 1); for (uint j = 0; j < rets.length; j++) { matrix[i][j + 1] = int256(rets[j]) - gas; atLeastOnePositive = atLeastOnePositive || (matrix[i][j + 1] > 0); } } if (!atLeastOnePositive) { for (uint i = 0; i < DEXES_COUNT; i++) { for (uint j = 1; j < parts + 1; j++) { if (matrix[i][j] == 0) { matrix[i][j] = VERY_NEGATIVE_VALUE; } } } } (, distribution) = _findBestDistribution(parts, matrix); (returnAmount, estimateGasAmount) = _getReturnAndGasByDistribution( Args({ fromToken: fromToken, destToken: destToken, amount: amount, parts: parts, flags: flags, destTokenEthPriceTimesGasPrice: destTokenEthPriceTimesGasPrice, distribution: distribution, matrix: matrix, gases: gases, reserves: reserves }) ); return (returnAmount, estimateGasAmount, distribution); } struct Args { IERC20 fromToken; IERC20 destToken; uint256 amount; uint256 parts; uint256 flags; uint256 destTokenEthPriceTimesGasPrice; uint256[] distribution; int256[][] matrix; uint256[DEXES_COUNT] gases; function(IERC20,IERC20,uint256,uint256,uint256) view returns(uint256[] memory, uint256)[DEXES_COUNT] reserves; } function _getReturnAndGasByDistribution( Args memory args ) internal view returns(uint256 returnAmount, uint256 estimateGasAmount) { bool[DEXES_COUNT] memory exact = [ true, // "Uniswap", false, // "Kyber", false, // "Bancor", false, // "Oasis", true, // "Curve Compound", true, // "Curve USDT", true, // "Curve Y", true, // "Curve Binance", true, // "Curve Synthetix", true, // "Uniswap Compound", true, // "Uniswap CHAI", true, // "Uniswap Aave", true, // "Mooniswap 1", true, // "Uniswap V2", true, // "Uniswap V2 (ETH)", true, // "Uniswap V2 (DAI)", true, // "Uniswap V2 (USDC)", true, // "Curve Pax", true, // "Curve RenBTC", true, // "Curve tBTC", true, // "Dforce XSwap", false, // "Shell", true, // "mStable", true, // "Curve sBTC" true, // "Balancer 1" true, // "Balancer 2" true, // "Balancer 3" true, // "Kyber 1" true, // "Kyber 2" true, // "Kyber 3" true, // "Kyber 4" true, // "Mooniswap 2" true, // "Mooniswap 3" true // "Mooniswap 4" ]; for (uint i = 0; i < DEXES_COUNT; i++) { if (args.distribution[i] > 0) { if (args.distribution[i] == args.parts || exact[i] || args.flags.check(FLAG_DISABLE_SPLIT_RECALCULATION)) { estimateGasAmount = estimateGasAmount.add(args.gases[i]); int256 value = args.matrix[i][args.distribution[i]]; returnAmount = returnAmount.add(uint256( (value == VERY_NEGATIVE_VALUE ? 0 : value) + int256(args.gases[i].mul(args.destTokenEthPriceTimesGasPrice).div(1e18)) )); } else { (uint256[] memory rets, uint256 gas) = args.reserves[i](args.fromToken, args.destToken, args.amount.mul(args.distribution[i]).div(args.parts), 1, args.flags); estimateGasAmount = estimateGasAmount.add(gas); returnAmount = returnAmount.add(rets[0]); } } } } function _getAllReserves(uint256 flags) internal pure returns(function(IERC20,IERC20,uint256,uint256,uint256) view returns(uint256[] memory, uint256)[DEXES_COUNT] memory) { bool invert = flags.check(FLAG_DISABLE_ALL_SPLIT_SOURCES); return [ invert != flags.check(FLAG_DISABLE_UNISWAP_ALL | FLAG_DISABLE_UNISWAP) ? _calculateNoReturn : calculateUniswap, _calculateNoReturn, // invert != flags.check(FLAG_DISABLE_KYBER) ? _calculateNoReturn : calculateKyber, invert != flags.check(FLAG_DISABLE_BANCOR) ? _calculateNoReturn : calculateBancor, invert != flags.check(FLAG_DISABLE_OASIS) ? _calculateNoReturn : calculateOasis, invert != flags.check(FLAG_DISABLE_CURVE_ALL | FLAG_DISABLE_CURVE_COMPOUND) ? _calculateNoReturn : calculateCurveCompound, invert != flags.check(FLAG_DISABLE_CURVE_ALL | FLAG_DISABLE_CURVE_USDT) ? _calculateNoReturn : calculateCurveUSDT, invert != flags.check(FLAG_DISABLE_CURVE_ALL | FLAG_DISABLE_CURVE_Y) ? _calculateNoReturn : calculateCurveY, invert != flags.check(FLAG_DISABLE_CURVE_ALL | FLAG_DISABLE_CURVE_BINANCE) ? _calculateNoReturn : calculateCurveBinance, invert != flags.check(FLAG_DISABLE_CURVE_ALL | FLAG_DISABLE_CURVE_SYNTHETIX) ? _calculateNoReturn : calculateCurveSynthetix, invert != flags.check(FLAG_DISABLE_UNISWAP_ALL | FLAG_DISABLE_UNISWAP_COMPOUND) ? _calculateNoReturn : calculateUniswapCompound, invert != flags.check(FLAG_DISABLE_UNISWAP_ALL | FLAG_DISABLE_UNISWAP_CHAI) ? _calculateNoReturn : calculateUniswapChai, invert != flags.check(FLAG_DISABLE_UNISWAP_ALL | FLAG_DISABLE_UNISWAP_AAVE) ? _calculateNoReturn : calculateUniswapAave, invert != flags.check(FLAG_DISABLE_MOONISWAP_ALL | FLAG_DISABLE_MOONISWAP) ? _calculateNoReturn : calculateMooniswap, invert != flags.check(FLAG_DISABLE_UNISWAP_V2_ALL | FLAG_DISABLE_UNISWAP_V2) ? _calculateNoReturn : calculateUniswapV2, invert != flags.check(FLAG_DISABLE_UNISWAP_V2_ALL | FLAG_DISABLE_UNISWAP_V2_ETH) ? _calculateNoReturn : calculateUniswapV2ETH, invert != flags.check(FLAG_DISABLE_UNISWAP_V2_ALL | FLAG_DISABLE_UNISWAP_V2_DAI) ? _calculateNoReturn : calculateUniswapV2DAI, invert != flags.check(FLAG_DISABLE_UNISWAP_V2_ALL | FLAG_DISABLE_UNISWAP_V2_USDC) ? _calculateNoReturn : calculateUniswapV2USDC, invert != flags.check(FLAG_DISABLE_CURVE_ALL | FLAG_DISABLE_CURVE_PAX) ? _calculateNoReturn : calculateCurvePAX, invert != flags.check(FLAG_DISABLE_CURVE_ALL | FLAG_DISABLE_CURVE_RENBTC) ? _calculateNoReturn : calculateCurveRenBTC, invert != flags.check(FLAG_DISABLE_CURVE_ALL | FLAG_DISABLE_CURVE_TBTC) ? _calculateNoReturn : calculateCurveTBTC, invert != flags.check(FLAG_DISABLE_DFORCE_SWAP) ? _calculateNoReturn : calculateDforceSwap, invert != flags.check(FLAG_DISABLE_SHELL) ? _calculateNoReturn : calculateShell, invert != flags.check(FLAG_DISABLE_MSTABLE_MUSD) ? _calculateNoReturn : calculateMStableMUSD, invert != flags.check(FLAG_DISABLE_CURVE_ALL | FLAG_DISABLE_CURVE_SBTC) ? _calculateNoReturn : calculateCurveSBTC, invert != flags.check(FLAG_DISABLE_BALANCER_ALL | FLAG_DISABLE_BALANCER_1) ? _calculateNoReturn : calculateBalancer1, invert != flags.check(FLAG_DISABLE_BALANCER_ALL | FLAG_DISABLE_BALANCER_2) ? _calculateNoReturn : calculateBalancer2, invert != flags.check(FLAG_DISABLE_BALANCER_ALL | FLAG_DISABLE_BALANCER_3) ? _calculateNoReturn : calculateBalancer3, invert != flags.check(FLAG_DISABLE_KYBER_ALL | FLAG_DISABLE_KYBER_1) ? _calculateNoReturn : calculateKyber1, invert != flags.check(FLAG_DISABLE_KYBER_ALL | FLAG_DISABLE_KYBER_2) ? _calculateNoReturn : calculateKyber2, invert != flags.check(FLAG_DISABLE_KYBER_ALL | FLAG_DISABLE_KYBER_3) ? _calculateNoReturn : calculateKyber3, invert != flags.check(FLAG_DISABLE_KYBER_ALL | FLAG_DISABLE_KYBER_4) ? _calculateNoReturn : calculateKyber4, invert != flags.check(FLAG_DISABLE_MOONISWAP_ALL | FLAG_DISABLE_MOONISWAP_ETH) ? _calculateNoReturn : calculateMooniswapOverETH, invert != flags.check(FLAG_DISABLE_MOONISWAP_ALL | FLAG_DISABLE_MOONISWAP_DAI) ? _calculateNoReturn : calculateMooniswapOverDAI, invert != flags.check(FLAG_DISABLE_MOONISWAP_ALL | FLAG_DISABLE_MOONISWAP_USDC) ? _calculateNoReturn : calculateMooniswapOverUSDC ]; } function _calculateNoGas( IERC20 /*fromToken*/, IERC20 /*destToken*/, uint256 /*amount*/, uint256 /*parts*/, uint256 /*destTokenEthPriceTimesGasPrice*/, uint256 /*flags*/, uint256 /*destTokenEthPrice*/ ) internal view returns(uint256[] memory /*rets*/, uint256 /*gas*/) { this; } // View Helpers struct Balances { uint256 src; uint256 dst; } function _calculateBalancer( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 poolIndex ) internal view returns(uint256[] memory rets, uint256 gas) { address[] memory pools = balancerRegistry.getBestPoolsWithLimit( address(fromToken.isETH() ? weth : fromToken), address(destToken.isETH() ? weth : destToken), poolIndex + 1 ); if (poolIndex >= pools.length) { return (new uint256[](parts), 0); } rets = balancerHelper.getReturns( IBalancerPool(pools[poolIndex]), fromToken.isETH() ? weth : fromToken, destToken.isETH() ? weth : destToken, _linearInterpolation(amount, parts) ); gas = 75_000 + (fromToken.isETH() || destToken.isETH() ? 0 : 65_000); } function calculateBalancer1( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { return _calculateBalancer( fromToken, destToken, amount, parts, 0 ); } function calculateBalancer2( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { return _calculateBalancer( fromToken, destToken, amount, parts, 1 ); } function calculateBalancer3( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { return _calculateBalancer( fromToken, destToken, amount, parts, 2 ); } function calculateMStableMUSD( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { rets = new uint256[](parts); if ((fromToken != usdc && fromToken != dai && fromToken != usdt && fromToken != tusd) || (destToken != usdc && destToken != dai && destToken != usdt && destToken != tusd)) { return (rets, 0); } for (uint i = 1; i <= parts; i *= 2) { (bool success, bytes memory data) = address(musd).staticcall(abi.encodeWithSelector( musd.getSwapOutput.selector, fromToken, destToken, amount.mul(parts.div(i)).div(parts) )); if (success && data.length > 0) { (,, uint256 maxRet) = abi.decode(data, (bool,string,uint256)); if (maxRet > 0) { for (uint j = 0; j < parts.div(i); j++) { rets[j] = maxRet.mul(j + 1).div(parts.div(i)); } break; } } } return ( rets, 700_000 ); } function _getCurvePoolInfo( ICurve curve, bool haveUnderlying ) internal view returns( uint256[8] memory balances, uint256[8] memory precisions, uint256[8] memory rates, uint256 amp, uint256 fee ) { uint256[8] memory underlying_balances; uint256[8] memory decimals; uint256[8] memory underlying_decimals; ( balances, underlying_balances, decimals, underlying_decimals, /*address lp_token*/, amp, fee ) = curveRegistry.get_pool_info(address(curve)); for (uint k = 0; k < 8 && balances[k] > 0; k++) { precisions[k] = 10 ** (18 - (haveUnderlying ? underlying_decimals : decimals)[k]); if (haveUnderlying) { rates[k] = underlying_balances[k].mul(1e18).div(balances[k]); } else { rates[k] = 1e18; } } } function _calculateCurveSelector( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, ICurve curve, bool haveUnderlying, IERC20[] memory tokens ) internal view returns(uint256[] memory rets) { rets = new uint256[](parts); int128 i = 0; int128 j = 0; for (uint t = 0; t < tokens.length; t++) { if (fromToken == tokens[t]) { i = int128(t + 1); } if (destToken == tokens[t]) { j = int128(t + 1); } } if (i == 0 || j == 0) { return rets; } bytes memory data = abi.encodePacked( uint256(haveUnderlying ? 1 : 0), uint256(i - 1), uint256(j - 1), _linearInterpolation100(amount, parts) ); ( uint256[8] memory balances, uint256[8] memory precisions, uint256[8] memory rates, uint256 amp, uint256 fee ) = _getCurvePoolInfo(curve, haveUnderlying); bool success; (success, data) = address(curveCalculator).staticcall( abi.encodePacked( abi.encodeWithSelector( curveCalculator.get_dy.selector, tokens.length, balances, amp, fee, rates, precisions ), data ) ); if (!success || data.length == 0) { return rets; } uint256[100] memory dy = abi.decode(data, (uint256[100])); for (uint t = 0; t < parts; t++) { rets[t] = dy[t]; } } function _linearInterpolation100( uint256 value, uint256 parts ) internal pure returns(uint256[100] memory rets) { for (uint i = 0; i < parts; i++) { rets[i] = value.mul(i + 1).div(parts); } } function calculateCurveCompound( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20[] memory tokens = new IERC20[](2); tokens[0] = dai; tokens[1] = usdc; return (_calculateCurveSelector( fromToken, destToken, amount, parts, curveCompound, true, tokens ), 720_000); } function calculateCurveUSDT( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20[] memory tokens = new IERC20[](3); tokens[0] = dai; tokens[1] = usdc; tokens[2] = usdt; return (_calculateCurveSelector( fromToken, destToken, amount, parts, curveUSDT, true, tokens ), 720_000); } function calculateCurveY( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20[] memory tokens = new IERC20[](4); tokens[0] = dai; tokens[1] = usdc; tokens[2] = usdt; tokens[3] = tusd; return (_calculateCurveSelector( fromToken, destToken, amount, parts, curveY, true, tokens ), 1_400_000); } function calculateCurveBinance( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20[] memory tokens = new IERC20[](4); tokens[0] = dai; tokens[1] = usdc; tokens[2] = usdt; tokens[3] = busd; return (_calculateCurveSelector( fromToken, destToken, amount, parts, curveBinance, true, tokens ), 1_400_000); } function calculateCurveSynthetix( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20[] memory tokens = new IERC20[](4); tokens[0] = dai; tokens[1] = usdc; tokens[2] = usdt; tokens[3] = susd; return (_calculateCurveSelector( fromToken, destToken, amount, parts, curveSynthetix, true, tokens ), 200_000); } function calculateCurvePAX( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20[] memory tokens = new IERC20[](4); tokens[0] = dai; tokens[1] = usdc; tokens[2] = usdt; tokens[3] = pax; return (_calculateCurveSelector( fromToken, destToken, amount, parts, curvePAX, true, tokens ), 1_000_000); } function calculateCurveRenBTC( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20[] memory tokens = new IERC20[](2); tokens[0] = renbtc; tokens[1] = wbtc; return (_calculateCurveSelector( fromToken, destToken, amount, parts, curveRenBTC, false, tokens ), 130_000); } function calculateCurveTBTC( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20[] memory tokens = new IERC20[](3); tokens[0] = tbtc; tokens[1] = wbtc; tokens[2] = hbtc; return (_calculateCurveSelector( fromToken, destToken, amount, parts, curveTBTC, false, tokens ), 145_000); } function calculateCurveSBTC( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20[] memory tokens = new IERC20[](3); tokens[0] = renbtc; tokens[1] = wbtc; tokens[2] = sbtc; return (_calculateCurveSelector( fromToken, destToken, amount, parts, curveSBTC, false, tokens ), 150_000); } function calculateShell( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { (bool success, bytes memory data) = address(shell).staticcall(abi.encodeWithSelector( shell.viewOriginTrade.selector, fromToken, destToken, amount )); if (!success || data.length == 0) { return (new uint256[](parts), 0); } uint256 maxRet = abi.decode(data, (uint256)); return (_linearInterpolation(maxRet, parts), 300_000); } function calculateDforceSwap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { (bool success, bytes memory data) = address(dforceSwap).staticcall( abi.encodeWithSelector( dforceSwap.getAmountByInput.selector, fromToken, destToken, amount ) ); if (!success || data.length == 0) { return (new uint256[](parts), 0); } uint256 maxRet = abi.decode(data, (uint256)); uint256 available = destToken.universalBalanceOf(address(dforceSwap)); if (maxRet > available) { return (new uint256[](parts), 0); } return (_linearInterpolation(maxRet, parts), 160_000); } function _calculateUniswapFormula(uint256 fromBalance, uint256 toBalance, uint256 amount) internal pure returns(uint256) { if (amount == 0) { return 0; } return amount.mul(toBalance).mul(997).div( fromBalance.mul(1000).add(amount.mul(997)) ); } function _calculateUniswap( IERC20 fromToken, IERC20 destToken, uint256[] memory amounts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { rets = amounts; if (!fromToken.isETH()) { IUniswapExchange fromExchange = uniswapFactory.getExchange(fromToken); if (fromExchange == IUniswapExchange(0)) { return (new uint256[](rets.length), 0); } uint256 fromTokenBalance = fromToken.universalBalanceOf(address(fromExchange)); uint256 fromEtherBalance = address(fromExchange).balance; for (uint i = 0; i < rets.length; i++) { rets[i] = _calculateUniswapFormula(fromTokenBalance, fromEtherBalance, rets[i]); } } if (!destToken.isETH()) { IUniswapExchange toExchange = uniswapFactory.getExchange(destToken); if (toExchange == IUniswapExchange(0)) { return (new uint256[](rets.length), 0); } uint256 toEtherBalance = address(toExchange).balance; uint256 toTokenBalance = destToken.universalBalanceOf(address(toExchange)); for (uint i = 0; i < rets.length; i++) { rets[i] = _calculateUniswapFormula(toEtherBalance, toTokenBalance, rets[i]); } } return (rets, fromToken.isETH() || destToken.isETH() ? 60_000 : 100_000); } function calculateUniswap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { return _calculateUniswap( fromToken, destToken, _linearInterpolation(amount, parts), flags ); } function _calculateUniswapWrapped( IERC20 fromToken, IERC20 midToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 midTokenPrice, uint256 flags, uint256 gas1, uint256 gas2 ) internal view returns(uint256[] memory rets, uint256 gas) { if (!fromToken.isETH() && destToken.isETH()) { (rets, gas) = _calculateUniswap( midToken, destToken, _linearInterpolation(amount.mul(1e18).div(midTokenPrice), parts), flags ); return (rets, gas + gas1); } else if (fromToken.isETH() && !destToken.isETH()) { (rets, gas) = _calculateUniswap( fromToken, midToken, _linearInterpolation(amount, parts), flags ); for (uint i = 0; i < parts; i++) { rets[i] = rets[i].mul(midTokenPrice).div(1e18); } return (rets, gas + gas2); } return (new uint256[](parts), 0); } function calculateUniswapCompound( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20 midPreToken; if (!fromToken.isETH() && destToken.isETH()) { midPreToken = fromToken; } else if (!destToken.isETH() && fromToken.isETH()) { midPreToken = destToken; } if (!midPreToken.isETH()) { ICompoundToken midToken = compoundRegistry.cTokenByToken(midPreToken); if (midToken != ICompoundToken(0)) { return _calculateUniswapWrapped( fromToken, midToken, destToken, amount, parts, midToken.exchangeRateStored(), flags, 200_000, 200_000 ); } } return (new uint256[](parts), 0); } function calculateUniswapChai( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { if (fromToken == dai && destToken.isETH() || fromToken.isETH() && destToken == dai) { return _calculateUniswapWrapped( fromToken, chai, destToken, amount, parts, chai.chaiPrice(), flags, 180_000, 160_000 ); } return (new uint256[](parts), 0); } function calculateUniswapAave( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { IERC20 midPreToken; if (!fromToken.isETH() && destToken.isETH()) { midPreToken = fromToken; } else if (!destToken.isETH() && fromToken.isETH()) { midPreToken = destToken; } if (!midPreToken.isETH()) { IAaveToken midToken = aaveRegistry.aTokenByToken(midPreToken); if (midToken != IAaveToken(0)) { return _calculateUniswapWrapped( fromToken, midToken, destToken, amount, parts, 1e18, flags, 310_000, 670_000 ); } } return (new uint256[](parts), 0); } function calculateKyber1( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { return _calculateKyber( fromToken, destToken, amount, parts, flags, 0xff4b796265722046707200000000000000000000000000000000000000000000 // 0x63825c174ab367968EC60f061753D3bbD36A0D8F ); } function calculateKyber2( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { return _calculateKyber( fromToken, destToken, amount, parts, flags, 0xffabcd0000000000000000000000000000000000000000000000000000000000 // 0x7a3370075a54B187d7bD5DceBf0ff2B5552d4F7D ); } function calculateKyber3( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { return _calculateKyber( fromToken, destToken, amount, parts, flags, 0xff4f6e65426974205175616e7400000000000000000000000000000000000000 // 0x4f32BbE8dFc9efD54345Fc936f9fEF1048746fCF ); } function calculateKyber4( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { bytes32 reserveId = _kyberReserveIdByTokens(fromToken, destToken); if (reserveId == 0) { return (new uint256[](parts), 0); } return _calculateKyber( fromToken, destToken, amount, parts, flags, reserveId ); } function _kyberGetRate( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/, bytes memory hint ) private view returns(uint256) { (, bytes memory data) = address(kyberNetworkProxy).staticcall( abi.encodeWithSelector( kyberNetworkProxy.getExpectedRateAfterFee.selector, fromToken, destToken, amount, 10, hint ) ); return (data.length == 32) ? abi.decode(data, (uint256)) : 0; } function _calculateKyber( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, bytes32 reserveId ) internal view returns(uint256[] memory rets, uint256 gas) { bytes memory fromHint; bytes memory destHint; { bytes32[] memory reserveIds = new bytes32[](1); reserveIds[0] = reserveId; (bool success, bytes memory data) = address(kyberHintHandler).staticcall( abi.encodeWithSelector( kyberHintHandler.buildTokenToEthHint.selector, fromToken, IKyberHintHandler.TradeType.MaskIn, reserveIds, new uint256[](0) ) ); fromHint = success ? abi.decode(data, (bytes)) : bytes(""); (success, data) = address(kyberHintHandler).staticcall( abi.encodeWithSelector( kyberHintHandler.buildEthToTokenHint.selector, destToken, IKyberHintHandler.TradeType.MaskIn, reserveIds, new uint256[](0) ) ); destHint = success ? abi.decode(data, (bytes)) : bytes(""); } uint256 fromTokenDecimals = 10 ** IERC20(fromToken).universalDecimals(); uint256 destTokenDecimals = 10 ** IERC20(destToken).universalDecimals(); rets = new uint256[](parts); for (uint i = 0; i < parts; i++) { if (i > 0 && rets[i - 1] == 0) { break; } rets[i] = amount.mul(i + 1).div(parts); if (!fromToken.isETH()) { if (fromHint.length == 0) { rets[i] = 0; break; } uint256 rate = _kyberGetRate( fromToken, ETH_ADDRESS, rets[i], flags, fromHint ); rets[i] = rate.mul(rets[i]).div(fromTokenDecimals); } if (!destToken.isETH() && rets[i] > 0) { if (destHint.length == 0) { rets[i] = 0; break; } uint256 rate = _kyberGetRate( ETH_ADDRESS, destToken, rets[i], 10, destHint ); rets[i] = rate.mul(rets[i]).mul(destTokenDecimals).div(1e36); } } return (rets, 100_000); } function calculateBancor( IERC20 /*fromToken*/, IERC20 /*destToken*/, uint256 /*amount*/, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { return (new uint256[](parts), 0); // IBancorNetwork bancorNetwork = IBancorNetwork(bancorContractRegistry.addressOf("BancorNetwork")); // address[] memory path = bancorFinder.buildBancorPath( // fromToken.isETH() ? bancorEtherToken : fromToken, // destToken.isETH() ? bancorEtherToken : destToken // ); // rets = _linearInterpolation(amount, parts); // for (uint i = 0; i < parts; i++) { // (bool success, bytes memory data) = address(bancorNetwork).staticcall.gas(500000)( // abi.encodeWithSelector( // bancorNetwork.getReturnByPath.selector, // path, // rets[i] // ) // ); // if (!success || data.length == 0) { // for (; i < parts; i++) { // rets[i] = 0; // } // break; // } else { // (uint256 ret,) = abi.decode(data, (uint256,uint256)); // rets[i] = ret; // } // } // return (rets, path.length.mul(150_000)); } function calculateOasis( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { rets = _linearInterpolation(amount, parts); for (uint i = 0; i < parts; i++) { (bool success, bytes memory data) = address(oasisExchange).staticcall.gas(500000)( abi.encodeWithSelector( oasisExchange.getBuyAmount.selector, destToken.isETH() ? weth : destToken, fromToken.isETH() ? weth : fromToken, rets[i] ) ); if (!success || data.length == 0) { for (; i < parts; i++) { rets[i] = 0; } break; } else { rets[i] = abi.decode(data, (uint256)); } } return (rets, 500_000); } function calculateMooniswapMany( IERC20 fromToken, IERC20 destToken, uint256[] memory amounts ) internal view returns(uint256[] memory rets, uint256 gas) { rets = new uint256[](amounts.length); IMooniswap mooniswap = mooniswapRegistry.pools( fromToken.isETH() ? ZERO_ADDRESS : fromToken, destToken.isETH() ? ZERO_ADDRESS : destToken ); if (mooniswap == IMooniswap(0)) { return (rets, 0); } uint256 fee = mooniswap.fee(); uint256 fromBalance = mooniswap.getBalanceForAddition(fromToken.isETH() ? ZERO_ADDRESS : fromToken); uint256 destBalance = mooniswap.getBalanceForRemoval(destToken.isETH() ? ZERO_ADDRESS : destToken); if (fromBalance == 0 || destBalance == 0) { return (rets, 0); } for (uint i = 0; i < amounts.length; i++) { uint256 amount = amounts[i].sub(amounts[i].mul(fee).div(1e18)); rets[i] = amount.mul(destBalance).div( fromBalance.add(amount) ); } return (rets, (fromToken.isETH() || destToken.isETH()) ? 80_000 : 110_000); } function calculateMooniswap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { return calculateMooniswapMany( fromToken, destToken, _linearInterpolation(amount, parts) ); } function calculateMooniswapOverETH( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { if (fromToken.isETH() || destToken.isETH()) { return (new uint256[](parts), 0); } (uint256[] memory results, uint256 gas1) = calculateMooniswap(fromToken, ZERO_ADDRESS, amount, parts, flags); (rets, gas) = calculateMooniswapMany(ZERO_ADDRESS, destToken, results); gas = gas.add(gas1); } function calculateMooniswapOverDAI( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { if (fromToken == dai || destToken == dai) { return (new uint256[](parts), 0); } (uint256[] memory results, uint256 gas1) = calculateMooniswap(fromToken, dai, amount, parts, flags); (rets, gas) = calculateMooniswapMany(dai, destToken, results); gas = gas.add(gas1); } function calculateMooniswapOverUSDC( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { if (fromToken == usdc || destToken == usdc) { return (new uint256[](parts), 0); } (uint256[] memory results, uint256 gas1) = calculateMooniswap(fromToken, usdc, amount, parts, flags); (rets, gas) = calculateMooniswapMany(usdc, destToken, results); gas = gas.add(gas1); } function calculateUniswapV2( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { return _calculateUniswapV2( fromToken, destToken, _linearInterpolation(amount, parts), flags ); } function calculateUniswapV2ETH( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { if (fromToken.isETH() || fromToken == weth || destToken.isETH() || destToken == weth) { return (new uint256[](parts), 0); } return _calculateUniswapV2OverMidToken( fromToken, weth, destToken, amount, parts, flags ); } function calculateUniswapV2DAI( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { if (fromToken == dai || destToken == dai) { return (new uint256[](parts), 0); } return _calculateUniswapV2OverMidToken( fromToken, dai, destToken, amount, parts, flags ); } function calculateUniswapV2USDC( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { if (fromToken == usdc || destToken == usdc) { return (new uint256[](parts), 0); } return _calculateUniswapV2OverMidToken( fromToken, usdc, destToken, amount, parts, flags ); } function _calculateUniswapV2( IERC20 fromToken, IERC20 destToken, uint256[] memory amounts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { rets = new uint256[](amounts.length); IERC20 fromTokenReal = fromToken.isETH() ? weth : fromToken; IERC20 destTokenReal = destToken.isETH() ? weth : destToken; IUniswapV2Exchange exchange = uniswapV2.getPair(fromTokenReal, destTokenReal); if (exchange != IUniswapV2Exchange(0)) { uint256 fromTokenBalance = fromTokenReal.universalBalanceOf(address(exchange)); uint256 destTokenBalance = destTokenReal.universalBalanceOf(address(exchange)); for (uint i = 0; i < amounts.length; i++) { rets[i] = _calculateUniswapFormula(fromTokenBalance, destTokenBalance, amounts[i]); } return (rets, 50_000); } } function _calculateUniswapV2OverMidToken( IERC20 fromToken, IERC20 midToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) internal view returns(uint256[] memory rets, uint256 gas) { rets = _linearInterpolation(amount, parts); uint256 gas1; uint256 gas2; (rets, gas1) = _calculateUniswapV2(fromToken, midToken, rets, flags); (rets, gas2) = _calculateUniswapV2(midToken, destToken, rets, flags); return (rets, gas1 + gas2); } function _calculateNoReturn( IERC20 /*fromToken*/, IERC20 /*destToken*/, uint256 /*amount*/, uint256 parts, uint256 /*flags*/ ) internal view returns(uint256[] memory rets, uint256 gas) { this; return (new uint256[](parts), 0); } } contract OneSplitBaseWrap is IOneSplit, OneSplitRoot { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags // See constants in IOneSplit.sol ) internal { if (fromToken == destToken) { return; } _swapFloor( fromToken, destToken, amount, distribution, flags ); } function _swapFloor( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 /*flags*/ // See constants in IOneSplit.sol ) internal; } contract OneSplit is IOneSplit, OneSplitRoot { IOneSplitView public oneSplitView; constructor(IOneSplitView _oneSplitView) public { oneSplitView = _oneSplitView; } function() external payable { // solium-disable-next-line security/no-tx-origin require(msg.sender != tx.origin); } function getExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) public view returns( uint256 returnAmount, uint256[] memory distribution ) { (returnAmount, , distribution) = getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, 0 ); } function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return oneSplitView.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 minReturn, uint256[] memory distribution, uint256 flags // See constants in IOneSplit.sol ) public payable returns(uint256 returnAmount) { if (fromToken == destToken) { return amount; } function(IERC20,IERC20,uint256,uint256)[DEXES_COUNT] memory reserves = [ _swapOnUniswap, _swapOnNowhere, _swapOnBancor, _swapOnOasis, _swapOnCurveCompound, _swapOnCurveUSDT, _swapOnCurveY, _swapOnCurveBinance, _swapOnCurveSynthetix, _swapOnUniswapCompound, _swapOnUniswapChai, _swapOnUniswapAave, _swapOnMooniswap, _swapOnUniswapV2, _swapOnUniswapV2ETH, _swapOnUniswapV2DAI, _swapOnUniswapV2USDC, _swapOnCurvePAX, _swapOnCurveRenBTC, _swapOnCurveTBTC, _swapOnDforceSwap, _swapOnShell, _swapOnMStableMUSD, _swapOnCurveSBTC, _swapOnBalancer1, _swapOnBalancer2, _swapOnBalancer3, _swapOnKyber1, _swapOnKyber2, _swapOnKyber3, _swapOnKyber4, _swapOnMooniswapETH, _swapOnMooniswapDAI, _swapOnMooniswapUSDC ]; require(distribution.length <= reserves.length, "OneSplit: Distribution array should not exceed reserves array size"); uint256 parts = 0; uint256 lastNonZeroIndex = 0; for (uint i = 0; i < distribution.length; i++) { if (distribution[i] > 0) { parts = parts.add(distribution[i]); lastNonZeroIndex = i; } } if (parts == 0) { if (fromToken.isETH()) { msg.sender.transfer(msg.value); return msg.value; } return amount; } fromToken.universalTransferFrom(msg.sender, address(this), amount); uint256 remainingAmount = fromToken.universalBalanceOf(address(this)); for (uint i = 0; i < distribution.length; i++) { if (distribution[i] == 0) { continue; } uint256 swapAmount = amount.mul(distribution[i]).div(parts); if (i == lastNonZeroIndex) { swapAmount = remainingAmount; } remainingAmount -= swapAmount; reserves[i](fromToken, destToken, swapAmount, flags); } returnAmount = destToken.universalBalanceOf(address(this)); require(returnAmount >= minReturn, "OneSplit: Return amount was not enough"); destToken.universalTransfer(msg.sender, returnAmount); fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this))); } // Swap helpers function _swapOnCurveCompound( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { int128 i = (fromToken == dai ? 1 : 0) + (fromToken == usdc ? 2 : 0); int128 j = (destToken == dai ? 1 : 0) + (destToken == usdc ? 2 : 0); if (i == 0 || j == 0) { return; } fromToken.universalApprove(address(curveCompound), amount); curveCompound.exchange_underlying(i - 1, j - 1, amount, 0); } function _swapOnCurveUSDT( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { int128 i = (fromToken == dai ? 1 : 0) + (fromToken == usdc ? 2 : 0) + (fromToken == usdt ? 3 : 0); int128 j = (destToken == dai ? 1 : 0) + (destToken == usdc ? 2 : 0) + (destToken == usdt ? 3 : 0); if (i == 0 || j == 0) { return; } fromToken.universalApprove(address(curveUSDT), amount); curveUSDT.exchange_underlying(i - 1, j - 1, amount, 0); } function _swapOnCurveY( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { int128 i = (fromToken == dai ? 1 : 0) + (fromToken == usdc ? 2 : 0) + (fromToken == usdt ? 3 : 0) + (fromToken == tusd ? 4 : 0); int128 j = (destToken == dai ? 1 : 0) + (destToken == usdc ? 2 : 0) + (destToken == usdt ? 3 : 0) + (destToken == tusd ? 4 : 0); if (i == 0 || j == 0) { return; } fromToken.universalApprove(address(curveY), amount); curveY.exchange_underlying(i - 1, j - 1, amount, 0); } function _swapOnCurveBinance( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { int128 i = (fromToken == dai ? 1 : 0) + (fromToken == usdc ? 2 : 0) + (fromToken == usdt ? 3 : 0) + (fromToken == busd ? 4 : 0); int128 j = (destToken == dai ? 1 : 0) + (destToken == usdc ? 2 : 0) + (destToken == usdt ? 3 : 0) + (destToken == busd ? 4 : 0); if (i == 0 || j == 0) { return; } fromToken.universalApprove(address(curveBinance), amount); curveBinance.exchange_underlying(i - 1, j - 1, amount, 0); } function _swapOnCurveSynthetix( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { int128 i = (fromToken == dai ? 1 : 0) + (fromToken == usdc ? 2 : 0) + (fromToken == usdt ? 3 : 0) + (fromToken == susd ? 4 : 0); int128 j = (destToken == dai ? 1 : 0) + (destToken == usdc ? 2 : 0) + (destToken == usdt ? 3 : 0) + (destToken == susd ? 4 : 0); if (i == 0 || j == 0) { return; } fromToken.universalApprove(address(curveSynthetix), amount); curveSynthetix.exchange_underlying(i - 1, j - 1, amount, 0); } function _swapOnCurvePAX( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { int128 i = (fromToken == dai ? 1 : 0) + (fromToken == usdc ? 2 : 0) + (fromToken == usdt ? 3 : 0) + (fromToken == pax ? 4 : 0); int128 j = (destToken == dai ? 1 : 0) + (destToken == usdc ? 2 : 0) + (destToken == usdt ? 3 : 0) + (destToken == pax ? 4 : 0); if (i == 0 || j == 0) { return; } fromToken.universalApprove(address(curvePAX), amount); curvePAX.exchange_underlying(i - 1, j - 1, amount, 0); } function _swapOnShell( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { fromToken.universalApprove(address(shell), amount); shell.swapByOrigin( address(fromToken), address(destToken), amount, 0, now + 50 ); } function _swapOnMStableMUSD( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { fromToken.universalApprove(address(musd), amount); musd.swap( fromToken, destToken, amount, address(this) ); } function _swapOnCurveRenBTC( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { int128 i = (fromToken == renbtc ? 1 : 0) + (fromToken == wbtc ? 2 : 0); int128 j = (destToken == renbtc ? 1 : 0) + (destToken == wbtc ? 2 : 0); if (i == 0 || j == 0) { return; } fromToken.universalApprove(address(curveRenBTC), amount); curveRenBTC.exchange(i - 1, j - 1, amount, 0); } function _swapOnCurveTBTC( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { int128 i = (fromToken == tbtc ? 1 : 0) + (fromToken == wbtc ? 2 : 0) + (fromToken == hbtc ? 3 : 0); int128 j = (destToken == tbtc ? 1 : 0) + (destToken == wbtc ? 2 : 0) + (destToken == hbtc ? 3 : 0); if (i == 0 || j == 0) { return; } fromToken.universalApprove(address(curveTBTC), amount); curveTBTC.exchange(i - 1, j - 1, amount, 0); } function _swapOnCurveSBTC( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { int128 i = (fromToken == renbtc ? 1 : 0) + (fromToken == wbtc ? 2 : 0) + (fromToken == sbtc ? 3 : 0); int128 j = (destToken == renbtc ? 1 : 0) + (destToken == wbtc ? 2 : 0) + (destToken == sbtc ? 3 : 0); if (i == 0 || j == 0) { return; } fromToken.universalApprove(address(curveSBTC), amount); curveSBTC.exchange(i - 1, j - 1, amount, 0); } function _swapOnDforceSwap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { fromToken.universalApprove(address(dforceSwap), amount); dforceSwap.swap(fromToken, destToken, amount); } function _swapOnUniswap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { uint256 returnAmount = amount; if (!fromToken.isETH()) { IUniswapExchange fromExchange = uniswapFactory.getExchange(fromToken); if (fromExchange != IUniswapExchange(0)) { fromToken.universalApprove(address(fromExchange), returnAmount); returnAmount = fromExchange.tokenToEthSwapInput(returnAmount, 1, now); } } if (!destToken.isETH()) { IUniswapExchange toExchange = uniswapFactory.getExchange(destToken); if (toExchange != IUniswapExchange(0)) { returnAmount = toExchange.ethToTokenSwapInput.value(returnAmount)(1, now); } } } function _swapOnUniswapCompound( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { if (!fromToken.isETH()) { ICompoundToken fromCompound = compoundRegistry.cTokenByToken(fromToken); fromToken.universalApprove(address(fromCompound), amount); fromCompound.mint(amount); _swapOnUniswap(IERC20(fromCompound), destToken, IERC20(fromCompound).universalBalanceOf(address(this)), flags); return; } if (!destToken.isETH()) { ICompoundToken toCompound = compoundRegistry.cTokenByToken(destToken); _swapOnUniswap(fromToken, IERC20(toCompound), amount, flags); toCompound.redeem(IERC20(toCompound).universalBalanceOf(address(this))); destToken.universalBalanceOf(address(this)); return; } } function _swapOnUniswapChai( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { if (fromToken == dai) { fromToken.universalApprove(address(chai), amount); chai.join(address(this), amount); _swapOnUniswap(IERC20(chai), destToken, IERC20(chai).universalBalanceOf(address(this)), flags); return; } if (destToken == dai) { _swapOnUniswap(fromToken, IERC20(chai), amount, flags); chai.exit(address(this), chai.balanceOf(address(this))); return; } } function _swapOnUniswapAave( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { if (!fromToken.isETH()) { IAaveToken fromAave = aaveRegistry.aTokenByToken(fromToken); fromToken.universalApprove(aave.core(), amount); aave.deposit(fromToken, amount, 1101); _swapOnUniswap(IERC20(fromAave), destToken, IERC20(fromAave).universalBalanceOf(address(this)), flags); return; } if (!destToken.isETH()) { IAaveToken toAave = aaveRegistry.aTokenByToken(destToken); _swapOnUniswap(fromToken, IERC20(toAave), amount, flags); toAave.redeem(toAave.balanceOf(address(this))); return; } } function _swapOnMooniswap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { IMooniswap mooniswap = mooniswapRegistry.pools( fromToken.isETH() ? ZERO_ADDRESS : fromToken, destToken.isETH() ? ZERO_ADDRESS : destToken ); fromToken.universalApprove(address(mooniswap), amount); mooniswap.swap.value(fromToken.isETH() ? amount : 0)( fromToken.isETH() ? ZERO_ADDRESS : fromToken, destToken.isETH() ? ZERO_ADDRESS : destToken, amount, 0, 0x4D37f28D2db99e8d35A6C725a5f1749A085850a3 ); } function _swapOnMooniswapETH( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnMooniswap(fromToken, ZERO_ADDRESS, amount, flags); _swapOnMooniswap(ZERO_ADDRESS, destToken, address(this).balance, flags); } function _swapOnMooniswapDAI( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnMooniswap(fromToken, dai, amount, flags); _swapOnMooniswap(dai, destToken, dai.balanceOf(address(this)), flags); } function _swapOnMooniswapUSDC( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnMooniswap(fromToken, usdc, amount, flags); _swapOnMooniswap(usdc, destToken, usdc.balanceOf(address(this)), flags); } function _swapOnNowhere( IERC20 /*fromToken*/, IERC20 /*destToken*/, uint256 /*amount*/, uint256 /*flags*/ ) internal { revert("This source was deprecated"); } function _swapOnKyber1( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnKyber( fromToken, destToken, amount, flags, 0xff4b796265722046707200000000000000000000000000000000000000000000 ); } function _swapOnKyber2( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnKyber( fromToken, destToken, amount, flags, 0xffabcd0000000000000000000000000000000000000000000000000000000000 ); } function _swapOnKyber3( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnKyber( fromToken, destToken, amount, flags, 0xff4f6e65426974205175616e7400000000000000000000000000000000000000 ); } function _swapOnKyber4( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnKyber( fromToken, destToken, amount, flags, _kyberReserveIdByTokens(fromToken, destToken) ); } function _swapOnKyber( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/, bytes32 reserveId ) internal { uint256 returnAmount = amount; bytes32[] memory reserveIds = new bytes32[](1); reserveIds[0] = reserveId; if (!fromToken.isETH()) { bytes memory fromHint = kyberHintHandler.buildTokenToEthHint( fromToken, IKyberHintHandler.TradeType.MaskIn, reserveIds, new uint256[](0) ); fromToken.universalApprove(address(kyberNetworkProxy), amount); returnAmount = kyberNetworkProxy.tradeWithHintAndFee( fromToken, returnAmount, ETH_ADDRESS, address(this), uint256(-1), 0, 0x4D37f28D2db99e8d35A6C725a5f1749A085850a3, 10, fromHint ); } if (!destToken.isETH()) { bytes memory destHint = kyberHintHandler.buildEthToTokenHint( destToken, IKyberHintHandler.TradeType.MaskIn, reserveIds, new uint256[](0) ); returnAmount = kyberNetworkProxy.tradeWithHintAndFee.value(returnAmount)( ETH_ADDRESS, returnAmount, destToken, address(this), uint256(-1), 0, 0x4D37f28D2db99e8d35A6C725a5f1749A085850a3, 10, destHint ); } } function _swapOnBancor( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { IBancorNetwork bancorNetwork = IBancorNetwork(bancorContractRegistry.addressOf("BancorNetwork")); address[] memory path = bancorNetworkPathFinder.generatePath( fromToken.isETH() ? bancorEtherToken : fromToken, destToken.isETH() ? bancorEtherToken : destToken ); fromToken.universalApprove(address(bancorNetwork), amount); bancorNetwork.convert.value(fromToken.isETH() ? amount : 0)(path, amount, 1); } function _swapOnOasis( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal { if (fromToken.isETH()) { weth.deposit.value(amount)(); } IERC20 approveToken = fromToken.isETH() ? weth : fromToken; approveToken.universalApprove(address(oasisExchange), amount); oasisExchange.sellAllAmount( fromToken.isETH() ? weth : fromToken, amount, destToken.isETH() ? weth : destToken, 1 ); if (destToken.isETH()) { weth.withdraw(weth.balanceOf(address(this))); } } function _swapOnUniswapV2Internal( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/ ) internal returns(uint256 returnAmount) { if (fromToken.isETH()) { weth.deposit.value(amount)(); } IERC20 fromTokenReal = fromToken.isETH() ? weth : fromToken; IERC20 toTokenReal = destToken.isETH() ? weth : destToken; IUniswapV2Exchange exchange = uniswapV2.getPair(fromTokenReal, toTokenReal); returnAmount = exchange.getReturn(fromTokenReal, toTokenReal, amount); fromTokenReal.universalTransfer(address(exchange), amount); if (uint256(address(fromTokenReal)) < uint256(address(toTokenReal))) { exchange.swap(0, returnAmount, address(this), ""); } else { exchange.swap(returnAmount, 0, address(this), ""); } if (destToken.isETH()) { weth.withdraw(weth.balanceOf(address(this))); } } function _swapOnUniswapV2OverMid( IERC20 fromToken, IERC20 midToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnUniswapV2Internal( midToken, destToken, _swapOnUniswapV2Internal( fromToken, midToken, amount, flags ), flags ); } function _swapOnUniswapV2( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnUniswapV2Internal( fromToken, destToken, amount, flags ); } function _swapOnUniswapV2ETH( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnUniswapV2OverMid( fromToken, weth, destToken, amount, flags ); } function _swapOnUniswapV2DAI( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnUniswapV2OverMid( fromToken, dai, destToken, amount, flags ); } function _swapOnUniswapV2USDC( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnUniswapV2OverMid( fromToken, usdc, destToken, amount, flags ); } function _swapOnBalancerX( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 /*flags*/, uint256 poolIndex ) internal { address[] memory pools = balancerRegistry.getBestPoolsWithLimit( address(fromToken.isETH() ? weth : fromToken), address(destToken.isETH() ? weth : destToken), poolIndex + 1 ); if (fromToken.isETH()) { weth.deposit.value(amount)(); } (fromToken.isETH() ? weth : fromToken).universalApprove(pools[poolIndex], amount); IBalancerPool(pools[poolIndex]).swapExactAmountIn( fromToken.isETH() ? weth : fromToken, amount, destToken.isETH() ? weth : destToken, 0, uint256(-1) ); if (destToken.isETH()) { weth.withdraw(weth.balanceOf(address(this))); } } function _swapOnBalancer1( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnBalancerX(fromToken, destToken, amount, flags, 0); } function _swapOnBalancer2( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnBalancerX(fromToken, destToken, amount, flags, 1); } function _swapOnBalancer3( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 flags ) internal { _swapOnBalancerX(fromToken, destToken, amount, flags, 2); } } // File: contracts/OneSplitCompound.sol pragma solidity ^0.5.0; contract OneSplitCompoundView is OneSplitViewWrapBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return _compoundGetExpectedReturn( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function _compoundGetExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) private view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_COMPOUND)) { IERC20 underlying = compoundRegistry.tokenByCToken(ICompoundToken(address(fromToken))); if (underlying != IERC20(0)) { uint256 compoundRate = ICompoundToken(address(fromToken)).exchangeRateStored(); (returnAmount, estimateGasAmount, distribution) = _compoundGetExpectedReturn( underlying, destToken, amount.mul(compoundRate).div(1e18), parts, flags, destTokenEthPriceTimesGasPrice ); return (returnAmount, estimateGasAmount + 295_000, distribution); } underlying = compoundRegistry.tokenByCToken(ICompoundToken(address(destToken))); if (underlying != IERC20(0)) { uint256 _destTokenEthPriceTimesGasPrice = destTokenEthPriceTimesGasPrice; uint256 compoundRate = ICompoundToken(address(destToken)).exchangeRateStored(); (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( fromToken, underlying, amount, parts, flags, _destTokenEthPriceTimesGasPrice.mul(compoundRate).div(1e18) ); return (returnAmount.mul(1e18).div(compoundRate), estimateGasAmount + 430_000, distribution); } } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitCompound is OneSplitBaseWrap { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { _compoundSwap( fromToken, destToken, amount, distribution, flags ); } function _compoundSwap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) private { if (fromToken == destToken) { return; } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_COMPOUND)) { IERC20 underlying = compoundRegistry.tokenByCToken(ICompoundToken(address(fromToken))); if (underlying != IERC20(0)) { ICompoundToken(address(fromToken)).redeem(amount); uint256 underlyingAmount = underlying.universalBalanceOf(address(this)); return _compoundSwap( underlying, destToken, underlyingAmount, distribution, flags ); } underlying = compoundRegistry.tokenByCToken(ICompoundToken(address(destToken))); if (underlying != IERC20(0)) { super._swap( fromToken, underlying, amount, distribution, flags ); uint256 underlyingAmount = underlying.universalBalanceOf(address(this)); if (underlying.isETH()) { cETH.mint.value(underlyingAmount)(); } else { underlying.universalApprove(address(destToken), underlyingAmount); ICompoundToken(address(destToken)).mint(underlyingAmount); } return; } } return super._swap( fromToken, destToken, amount, distribution, flags ); } } // File: contracts/interface/IFulcrum.sol pragma solidity ^0.5.0; contract IFulcrumToken is IERC20 { function tokenPrice() external view returns (uint256); function loanTokenAddress() external view returns (address); function mintWithEther(address receiver) external payable returns (uint256 mintAmount); function mint(address receiver, uint256 depositAmount) external returns (uint256 mintAmount); function burnToEther(address receiver, uint256 burnAmount) external returns (uint256 loanAmountPaid); function burn(address receiver, uint256 burnAmount) external returns (uint256 loanAmountPaid); } // File: contracts/OneSplitFulcrum.sol pragma solidity ^0.5.0; contract OneSplitFulcrumBase { using UniversalERC20 for IERC20; function _isFulcrumToken(IERC20 token) internal view returns(IERC20) { if (token.isETH()) { return IERC20(-1); } (bool success, bytes memory data) = address(token).staticcall.gas(5000)(abi.encodeWithSignature( "name()" )); if (!success) { return IERC20(-1); } bool foundBZX = false; for (uint i = 0; i + 6 < data.length; i++) { if (data[i + 0] == "F" && data[i + 1] == "u" && data[i + 2] == "l" && data[i + 3] == "c" && data[i + 4] == "r" && data[i + 5] == "u" && data[i + 6] == "m") { foundBZX = true; break; } } if (!foundBZX) { return IERC20(-1); } (success, data) = address(token).staticcall.gas(5000)(abi.encodeWithSelector( IFulcrumToken(address(token)).loanTokenAddress.selector )); if (!success) { return IERC20(-1); } return abi.decode(data, (IERC20)); } } contract OneSplitFulcrumView is OneSplitViewWrapBase, OneSplitFulcrumBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return _fulcrumGetExpectedReturn( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function _fulcrumGetExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) private view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_FULCRUM)) { IERC20 underlying = _isFulcrumToken(fromToken); if (underlying != IERC20(-1)) { uint256 fulcrumRate = IFulcrumToken(address(fromToken)).tokenPrice(); (returnAmount, estimateGasAmount, distribution) = _fulcrumGetExpectedReturn( underlying, destToken, amount.mul(fulcrumRate).div(1e18), parts, flags, destTokenEthPriceTimesGasPrice ); return (returnAmount, estimateGasAmount + 381_000, distribution); } underlying = _isFulcrumToken(destToken); if (underlying != IERC20(-1)) { uint256 _destTokenEthPriceTimesGasPrice = destTokenEthPriceTimesGasPrice; uint256 fulcrumRate = IFulcrumToken(address(destToken)).tokenPrice(); (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( fromToken, underlying, amount, parts, flags, _destTokenEthPriceTimesGasPrice.mul(fulcrumRate).div(1e18) ); return (returnAmount.mul(1e18).div(fulcrumRate), estimateGasAmount + 354_000, distribution); } } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitFulcrum is OneSplitBaseWrap, OneSplitFulcrumBase { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { _fulcrumSwap( fromToken, destToken, amount, distribution, flags ); } function _fulcrumSwap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) private { if (fromToken == destToken) { return; } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_FULCRUM)) { IERC20 underlying = _isFulcrumToken(fromToken); if (underlying != IERC20(-1)) { if (underlying.isETH()) { IFulcrumToken(address(fromToken)).burnToEther(address(this), amount); } else { IFulcrumToken(address(fromToken)).burn(address(this), amount); } uint256 underlyingAmount = underlying.universalBalanceOf(address(this)); return super._swap( underlying, destToken, underlyingAmount, distribution, flags ); } underlying = _isFulcrumToken(destToken); if (underlying != IERC20(-1)) { super._swap( fromToken, underlying, amount, distribution, flags ); uint256 underlyingAmount = underlying.universalBalanceOf(address(this)); if (underlying.isETH()) { IFulcrumToken(address(destToken)).mintWithEther.value(underlyingAmount)(address(this)); } else { underlying.universalApprove(address(destToken), underlyingAmount); IFulcrumToken(address(destToken)).mint(address(this), underlyingAmount); } return; } } return super._swap( fromToken, destToken, amount, distribution, flags ); } } // File: contracts/OneSplitChai.sol pragma solidity ^0.5.0; contract OneSplitChaiView is OneSplitViewWrapBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_CHAI)) { if (fromToken == IERC20(chai)) { (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( dai, destToken, chai.chaiToDai(amount), parts, flags, destTokenEthPriceTimesGasPrice ); return (returnAmount, estimateGasAmount + 197_000, distribution); } if (destToken == IERC20(chai)) { uint256 price = chai.chaiPrice(); (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( fromToken, dai, amount, parts, flags, destTokenEthPriceTimesGasPrice.mul(1e18).div(price) ); return (returnAmount.mul(price).div(1e18), estimateGasAmount + 168_000, distribution); } } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitChai is OneSplitBaseWrap { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { if (fromToken == destToken) { return; } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_CHAI)) { if (fromToken == IERC20(chai)) { chai.exit(address(this), amount); return super._swap( dai, destToken, dai.balanceOf(address(this)), distribution, flags ); } if (destToken == IERC20(chai)) { super._swap( fromToken, dai, amount, distribution, flags ); uint256 daiBalance = dai.balanceOf(address(this)); dai.universalApprove(address(chai), daiBalance); chai.join(address(this), daiBalance); return; } } return super._swap( fromToken, destToken, amount, distribution, flags ); } } // File: contracts/interface/IBdai.sol pragma solidity ^0.5.0; contract IBdai is IERC20 { function join(uint256) external; function exit(uint256) external; } // File: contracts/OneSplitBdai.sol pragma solidity ^0.5.0; contract OneSplitBdaiBase { IBdai internal constant bdai = IBdai(0x6a4FFAafa8DD400676Df8076AD6c724867b0e2e8); IERC20 internal constant btu = IERC20(0xb683D83a532e2Cb7DFa5275eED3698436371cc9f); } contract OneSplitBdaiView is OneSplitViewWrapBase, OneSplitBdaiBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_BDAI)) { if (fromToken == IERC20(bdai)) { (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( dai, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); return (returnAmount, estimateGasAmount + 227_000, distribution); } if (destToken == IERC20(bdai)) { (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( fromToken, dai, amount, parts, flags, destTokenEthPriceTimesGasPrice ); return (returnAmount, estimateGasAmount + 295_000, distribution); } } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitBdai is OneSplitBaseWrap, OneSplitBdaiBase { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { if (fromToken == destToken) { return; } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_BDAI)) { if (fromToken == IERC20(bdai)) { bdai.exit(amount); uint256 btuBalance = btu.balanceOf(address(this)); if (btuBalance > 0) { (,uint256[] memory btuDistribution) = getExpectedReturn( btu, destToken, btuBalance, 1, flags ); _swap( btu, destToken, btuBalance, btuDistribution, flags ); } return super._swap( dai, destToken, amount, distribution, flags ); } if (destToken == IERC20(bdai)) { super._swap(fromToken, dai, amount, distribution, flags); uint256 daiBalance = dai.balanceOf(address(this)); dai.universalApprove(address(bdai), daiBalance); bdai.join(daiBalance); return; } } return super._swap(fromToken, destToken, amount, distribution, flags); } } // File: contracts/interface/IIearn.sol pragma solidity ^0.5.0; contract IIearn is IERC20 { function token() external view returns(IERC20); function calcPoolValueInToken() external view returns(uint256); function deposit(uint256 _amount) external; function withdraw(uint256 _shares) external; } // File: contracts/OneSplitIearn.sol pragma solidity ^0.5.0; contract OneSplitIearnBase { function _yTokens() internal pure returns(IIearn[13] memory) { return [ IIearn(0x16de59092dAE5CcF4A1E6439D611fd0653f0Bd01), IIearn(0x04Aa51bbcB46541455cCF1B8bef2ebc5d3787EC9), IIearn(0x73a052500105205d34Daf004eAb301916DA8190f), IIearn(0x83f798e925BcD4017Eb265844FDDAbb448f1707D), IIearn(0xd6aD7a6750A7593E092a9B218d66C0A814a3436e), IIearn(0xF61718057901F84C4eEC4339EF8f0D86D2B45600), IIearn(0x04bC0Ab673d88aE9dbC9DA2380cB6B79C4BCa9aE), IIearn(0xC2cB1040220768554cf699b0d863A3cd4324ce32), IIearn(0xE6354ed5bC4b393a5Aad09f21c46E101e692d447), IIearn(0x26EA744E5B887E5205727f55dFBE8685e3b21951), IIearn(0x99d1Fa417f94dcD62BfE781a1213c092a47041Bc), IIearn(0x9777d7E2b60bB01759D0E2f8be2095df444cb07E), IIearn(0x1bE5d71F2dA660BFdee8012dDc58D024448A0A59) ]; } } contract OneSplitIearnView is OneSplitViewWrapBase, OneSplitIearnBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return _iearnGetExpectedReturn( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function _iearnGetExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) private view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (!flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == !flags.check(FLAG_DISABLE_IEARN)) { IIearn[13] memory yTokens = _yTokens(); for (uint i = 0; i < yTokens.length; i++) { if (fromToken == IERC20(yTokens[i])) { (returnAmount, estimateGasAmount, distribution) = _iearnGetExpectedReturn( yTokens[i].token(), destToken, amount .mul(yTokens[i].calcPoolValueInToken()) .div(yTokens[i].totalSupply()), parts, flags, destTokenEthPriceTimesGasPrice ); return (returnAmount, estimateGasAmount + 260_000, distribution); } } for (uint i = 0; i < yTokens.length; i++) { if (destToken == IERC20(yTokens[i])) { uint256 _destTokenEthPriceTimesGasPrice = destTokenEthPriceTimesGasPrice; IERC20 token = yTokens[i].token(); (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( fromToken, token, amount, parts, flags, _destTokenEthPriceTimesGasPrice .mul(yTokens[i].calcPoolValueInToken()) .div(yTokens[i].totalSupply()) ); return( returnAmount .mul(yTokens[i].totalSupply()) .div(yTokens[i].calcPoolValueInToken()), estimateGasAmount + 743_000, distribution ); } } } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitIearn is OneSplitBaseWrap, OneSplitIearnBase { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { _iearnSwap( fromToken, destToken, amount, distribution, flags ); } function _iearnSwap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) private { if (fromToken == destToken) { return; } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_IEARN)) { IIearn[13] memory yTokens = _yTokens(); for (uint i = 0; i < yTokens.length; i++) { if (fromToken == IERC20(yTokens[i])) { IERC20 underlying = yTokens[i].token(); yTokens[i].withdraw(amount); _iearnSwap(underlying, destToken, underlying.balanceOf(address(this)), distribution, flags); return; } } for (uint i = 0; i < yTokens.length; i++) { if (destToken == IERC20(yTokens[i])) { IERC20 underlying = yTokens[i].token(); super._swap(fromToken, underlying, amount, distribution, flags); uint256 underlyingBalance = underlying.balanceOf(address(this)); underlying.universalApprove(address(yTokens[i]), underlyingBalance); yTokens[i].deposit(underlyingBalance); return; } } } return super._swap(fromToken, destToken, amount, distribution, flags); } } // File: contracts/interface/IIdle.sol pragma solidity ^0.5.0; contract IIdle is IERC20 { function token() external view returns (IERC20); function tokenPrice() external view returns (uint256); function mintIdleToken(uint256 _amount, uint256[] calldata _clientProtocolAmounts) external returns (uint256 mintedTokens); function redeemIdleToken(uint256 _amount, bool _skipRebalance, uint256[] calldata _clientProtocolAmounts) external returns (uint256 redeemedTokens); } // File: contracts/OneSplitIdle.sol pragma solidity ^0.5.0; contract OneSplitIdleBase { function _idleTokens() internal pure returns(IIdle[8] memory) { // https://developers.idle.finance/contracts-and-codebase return [ // V3 IIdle(0x78751B12Da02728F467A44eAc40F5cbc16Bd7934), IIdle(0x12B98C621E8754Ae70d0fDbBC73D6208bC3e3cA6), IIdle(0x63D27B3DA94A9E871222CB0A32232674B02D2f2D), IIdle(0x1846bdfDB6A0f5c473dEc610144513bd071999fB), IIdle(0xcDdB1Bceb7a1979C6caa0229820707429dd3Ec6C), IIdle(0x42740698959761BAF1B06baa51EfBD88CB1D862B), // V2 IIdle(0x10eC0D497824e342bCB0EDcE00959142aAa766dD), IIdle(0xeB66ACc3d011056B00ea521F8203580C2E5d3991) ]; } } contract OneSplitIdleView is OneSplitViewWrapBase, OneSplitIdleBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return _idleGetExpectedReturn( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function _idleGetExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) internal view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (!flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == !flags.check(FLAG_DISABLE_IDLE)) { IIdle[8] memory tokens = _idleTokens(); for (uint i = 0; i < tokens.length; i++) { if (fromToken == IERC20(tokens[i])) { (returnAmount, estimateGasAmount, distribution) = _idleGetExpectedReturn( tokens[i].token(), destToken, amount.mul(tokens[i].tokenPrice()).div(1e18), parts, flags, destTokenEthPriceTimesGasPrice ); return (returnAmount, estimateGasAmount + 2_400_000, distribution); } } for (uint i = 0; i < tokens.length; i++) { if (destToken == IERC20(tokens[i])) { uint256 _destTokenEthPriceTimesGasPrice = destTokenEthPriceTimesGasPrice; uint256 _price = tokens[i].tokenPrice(); IERC20 token = tokens[i].token(); (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( fromToken, token, amount, parts, flags, _destTokenEthPriceTimesGasPrice.mul(_price).div(1e18) ); return (returnAmount.mul(1e18).div(_price), estimateGasAmount + 1_300_000, distribution); } } } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitIdle is OneSplitBaseWrap, OneSplitIdleBase { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { _idleSwap( fromToken, destToken, amount, distribution, flags ); } function _idleSwap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { if (!flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == !flags.check(FLAG_DISABLE_IDLE)) { IIdle[8] memory tokens = _idleTokens(); for (uint i = 0; i < tokens.length; i++) { if (fromToken == IERC20(tokens[i])) { IERC20 underlying = tokens[i].token(); uint256 minted = tokens[i].redeemIdleToken(amount, true, new uint256[](0)); _idleSwap(underlying, destToken, minted, distribution, flags); return; } } for (uint i = 0; i < tokens.length; i++) { if (destToken == IERC20(tokens[i])) { IERC20 underlying = tokens[i].token(); super._swap(fromToken, underlying, amount, distribution, flags); uint256 underlyingBalance = underlying.balanceOf(address(this)); underlying.universalApprove(address(tokens[i]), underlyingBalance); tokens[i].mintIdleToken(underlyingBalance, new uint256[](0)); return; } } } return super._swap(fromToken, destToken, amount, distribution, flags); } } // File: contracts/OneSplitAave.sol pragma solidity ^0.5.0; contract OneSplitAaveView is OneSplitViewWrapBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, // See constants in IOneSplit.sol uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return _aaveGetExpectedReturn( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function _aaveGetExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) private view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_AAVE)) { IERC20 underlying = aaveRegistry.tokenByAToken(IAaveToken(address(fromToken))); if (underlying != IERC20(0)) { (returnAmount, estimateGasAmount, distribution) = _aaveGetExpectedReturn( underlying, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); return (returnAmount, estimateGasAmount + 670_000, distribution); } underlying = aaveRegistry.tokenByAToken(IAaveToken(address(destToken))); if (underlying != IERC20(0)) { (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( fromToken, underlying, amount, parts, flags, destTokenEthPriceTimesGasPrice ); return (returnAmount, estimateGasAmount + 310_000, distribution); } } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitAave is OneSplitBaseWrap { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { _aaveSwap( fromToken, destToken, amount, distribution, flags ); } function _aaveSwap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) private { if (fromToken == destToken) { return; } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_AAVE)) { IERC20 underlying = aaveRegistry.tokenByAToken(IAaveToken(address(fromToken))); if (underlying != IERC20(0)) { IAaveToken(address(fromToken)).redeem(amount); return _aaveSwap( underlying, destToken, amount, distribution, flags ); } underlying = aaveRegistry.tokenByAToken(IAaveToken(address(destToken))); if (underlying != IERC20(0)) { super._swap( fromToken, underlying, amount, distribution, flags ); uint256 underlyingAmount = underlying.universalBalanceOf(address(this)); underlying.universalApprove(aave.core(), underlyingAmount); aave.deposit.value(underlying.isETH() ? underlyingAmount : 0)( underlying.isETH() ? ETH_ADDRESS : underlying, underlyingAmount, 1101 ); return; } } return super._swap( fromToken, destToken, amount, distribution, flags ); } } // File: contracts/OneSplitWeth.sol pragma solidity ^0.5.0; contract OneSplitWethView is OneSplitViewWrapBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return _wethGetExpectedReturn( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function _wethGetExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) private view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_WETH)) { if (fromToken == weth || fromToken == bancorEtherToken) { return super.getExpectedReturnWithGas(ETH_ADDRESS, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice); } if (destToken == weth || destToken == bancorEtherToken) { return super.getExpectedReturnWithGas(fromToken, ETH_ADDRESS, amount, parts, flags, destTokenEthPriceTimesGasPrice); } } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitWeth is OneSplitBaseWrap { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { _wethSwap( fromToken, destToken, amount, distribution, flags ); } function _wethSwap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) private { if (fromToken == destToken) { return; } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_WETH)) { if (fromToken == weth) { weth.withdraw(weth.balanceOf(address(this))); super._swap( ETH_ADDRESS, destToken, amount, distribution, flags ); return; } if (fromToken == bancorEtherToken) { bancorEtherToken.withdraw(bancorEtherToken.balanceOf(address(this))); super._swap( ETH_ADDRESS, destToken, amount, distribution, flags ); return; } if (destToken == weth) { _wethSwap( fromToken, ETH_ADDRESS, amount, distribution, flags ); weth.deposit.value(address(this).balance)(); return; } if (destToken == bancorEtherToken) { _wethSwap( fromToken, ETH_ADDRESS, amount, distribution, flags ); bancorEtherToken.deposit.value(address(this).balance)(); return; } } return super._swap( fromToken, destToken, amount, distribution, flags ); } } // File: contracts/OneSplitMStable.sol pragma solidity ^0.5.0; contract OneSplitMStableView is OneSplitViewWrapBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_MSTABLE_MUSD)) { if (fromToken == IERC20(musd)) { { (bool valid1,, uint256 res1,) = musd_helper.getRedeemValidity(musd, amount, destToken); if (valid1) { return (res1, 300_000, new uint256[](DEXES_COUNT)); } } (bool valid,, address token) = musd_helper.suggestRedeemAsset(musd); if (valid) { (,, returnAmount,) = musd_helper.getRedeemValidity(musd, amount, IERC20(token)); if (IERC20(token) != destToken) { (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( IERC20(token), destToken, returnAmount, parts, flags, destTokenEthPriceTimesGasPrice ); } else { distribution = new uint256[](DEXES_COUNT); } return (returnAmount, estimateGasAmount + 300_000, distribution); } } if (destToken == IERC20(musd)) { if (fromToken == usdc || fromToken == dai || fromToken == usdt || fromToken == tusd) { (,, returnAmount) = musd.getSwapOutput(fromToken, destToken, amount); return (returnAmount, 300_000, new uint256[](DEXES_COUNT)); } else { IERC20 _destToken = destToken; (bool valid,, address token) = musd_helper.suggestMintAsset(_destToken); if (valid) { if (IERC20(token) != fromToken) { (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( fromToken, IERC20(token), amount, parts, flags, _scaleDestTokenEthPriceTimesGasPrice( _destToken, IERC20(token), destTokenEthPriceTimesGasPrice ) ); } else { returnAmount = amount; } (,, returnAmount) = musd.getSwapOutput(IERC20(token), _destToken, returnAmount); return (returnAmount, estimateGasAmount + 300_000, distribution); } } } } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitMStable is OneSplitBaseWrap { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { if (fromToken == destToken) { return; } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_MSTABLE_MUSD)) { if (fromToken == IERC20(musd)) { if (destToken == usdc || destToken == dai || destToken == usdt || destToken == tusd) { (,,, uint256 result) = musd_helper.getRedeemValidity(fromToken, amount, destToken); musd.redeem( destToken, result ); } else { (,,, uint256 result) = musd_helper.getRedeemValidity(fromToken, amount, dai); musd.redeem( dai, result ); super._swap( dai, destToken, dai.balanceOf(address(this)), distribution, flags ); } return; } if (destToken == IERC20(musd)) { if (fromToken == usdc || fromToken == dai || fromToken == usdt || fromToken == tusd) { fromToken.universalApprove(address(musd), amount); musd.swap( fromToken, destToken, amount, address(this) ); } else { super._swap( fromToken, dai, amount, distribution, flags ); musd.swap( dai, destToken, dai.balanceOf(address(this)), address(this) ); } return; } } return super._swap( fromToken, destToken, amount, distribution, flags ); } } // File: contracts/interface/IDMM.sol pragma solidity ^0.5.0; interface IDMMController { function getUnderlyingTokenForDmm(IERC20 token) external view returns(IERC20); } contract IDMM is IERC20 { function getCurrentExchangeRate() public view returns(uint256); function mint(uint256 underlyingAmount) public returns(uint256); function redeem(uint256 amount) public returns(uint256); } // File: contracts/OneSplitDMM.sol pragma solidity ^0.5.0; contract OneSplitDMMBase { IDMMController internal constant _dmmController = IDMMController(0x4CB120Dd1D33C9A3De8Bc15620C7Cd43418d77E2); function _getDMMUnderlyingToken(IERC20 token) internal view returns(IERC20) { (bool success, bytes memory data) = address(_dmmController).staticcall( abi.encodeWithSelector( _dmmController.getUnderlyingTokenForDmm.selector, token ) ); if (!success || data.length == 0) { return IERC20(-1); } return abi.decode(data, (IERC20)); } function _getDMMExchangeRate(IDMM dmm) internal view returns(uint256) { (bool success, bytes memory data) = address(dmm).staticcall( abi.encodeWithSelector( dmm.getCurrentExchangeRate.selector ) ); if (!success || data.length == 0) { return 0; } return abi.decode(data, (uint256)); } } contract OneSplitDMMView is OneSplitViewWrapBase, OneSplitDMMBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return _dmmGetExpectedReturn( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function _dmmGetExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) private view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_DMM)) { IERC20 underlying = _getDMMUnderlyingToken(fromToken); if (underlying != IERC20(-1)) { if (underlying == weth) { underlying = ETH_ADDRESS; } IERC20 _fromToken = fromToken; (returnAmount, estimateGasAmount, distribution) = _dmmGetExpectedReturn( underlying, destToken, amount.mul(_getDMMExchangeRate(IDMM(address(_fromToken)))).div(1e18), parts, flags, destTokenEthPriceTimesGasPrice ); return (returnAmount, estimateGasAmount + 295_000, distribution); } underlying = _getDMMUnderlyingToken(destToken); if (underlying != IERC20(-1)) { if (underlying == weth) { underlying = ETH_ADDRESS; } uint256 price = _getDMMExchangeRate(IDMM(address(destToken))); (returnAmount, estimateGasAmount, distribution) = super.getExpectedReturnWithGas( fromToken, underlying, amount, parts, flags, destTokenEthPriceTimesGasPrice.mul(price).div(1e18) ); return ( returnAmount.mul(1e18).div(price), estimateGasAmount + 430_000, distribution ); } } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitDMM is OneSplitBaseWrap, OneSplitDMMBase { function _swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { _dmmSwap( fromToken, destToken, amount, distribution, flags ); } function _dmmSwap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) private { if (fromToken == destToken) { return; } if (flags.check(FLAG_DISABLE_ALL_WRAP_SOURCES) == flags.check(FLAG_DISABLE_DMM)) { IERC20 underlying = _getDMMUnderlyingToken(fromToken); if (underlying != IERC20(-1)) { IDMM(address(fromToken)).redeem(amount); uint256 balance = underlying.universalBalanceOf(address(this)); if (underlying == weth) { weth.withdraw(balance); } _dmmSwap( (underlying == weth) ? ETH_ADDRESS : underlying, destToken, balance, distribution, flags ); } underlying = _getDMMUnderlyingToken(destToken); if (underlying != IERC20(-1)) { super._swap( fromToken, (underlying == weth) ? ETH_ADDRESS : underlying, amount, distribution, flags ); uint256 underlyingAmount = ((underlying == weth) ? ETH_ADDRESS : underlying).universalBalanceOf(address(this)); if (underlying == weth) { weth.deposit.value(underlyingAmount); } underlying.universalApprove(address(destToken), underlyingAmount); IDMM(address(destToken)).mint(underlyingAmount); return; } } return super._swap( fromToken, destToken, amount, distribution, flags ); } } // File: @openzeppelin/contracts/math/Math.sol pragma solidity ^0.5.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } } // File: contracts/OneSplitMooniswapPoolToken.sol pragma solidity ^0.5.0; contract OneSplitMooniswapTokenBase { using SafeMath for uint256; using Math for uint256; using UniversalERC20 for IERC20; struct TokenInfo { IERC20 token; uint256 reserve; } struct PoolDetails { TokenInfo[2] tokens; uint256 totalSupply; } function _getPoolDetails(IMooniswap pool) internal view returns (PoolDetails memory details) { for (uint i = 0; i < 2; i++) { IERC20 token = pool.tokens(i); details.tokens[i] = TokenInfo({ token: token, reserve: token.universalBalanceOf(address(pool)) }); } details.totalSupply = IERC20(address(pool)).totalSupply(); } } contract OneSplitMooniswapTokenView is OneSplitViewWrapBase, OneSplitMooniswapTokenBase { function getExpectedReturnWithGas( IERC20 fromToken, IERC20 toToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns ( uint256 returnAmount, uint256, uint256[] memory distribution ) { if (fromToken.eq(toToken)) { return (amount, 0, new uint256[](DEXES_COUNT)); } if (!flags.check(FLAG_DISABLE_MOONISWAP_POOL_TOKEN)) { bool isPoolTokenFrom = mooniswapRegistry.isPool(address(fromToken)); bool isPoolTokenTo = mooniswapRegistry.isPool(address(toToken)); if (isPoolTokenFrom && isPoolTokenTo) { ( uint256 returnETHAmount, uint256[] memory poolTokenFromDistribution ) = _getExpectedReturnFromMooniswapPoolToken( fromToken, ETH_ADDRESS, amount, parts, FLAG_DISABLE_MOONISWAP_POOL_TOKEN ); ( uint256 returnPoolTokenToAmount, uint256[] memory poolTokenToDistribution ) = _getExpectedReturnToMooniswapPoolToken( ETH_ADDRESS, toToken, returnETHAmount, parts, FLAG_DISABLE_MOONISWAP_POOL_TOKEN ); for (uint i = 0; i < poolTokenToDistribution.length; i++) { poolTokenFromDistribution[i] |= poolTokenToDistribution[i] << 128; } return (returnPoolTokenToAmount, 0, poolTokenFromDistribution); } if (isPoolTokenFrom) { (returnAmount, distribution) = _getExpectedReturnFromMooniswapPoolToken( fromToken, toToken, amount, parts, FLAG_DISABLE_MOONISWAP_POOL_TOKEN ); return (returnAmount, 0, distribution); } if (isPoolTokenTo) { (returnAmount, distribution) = _getExpectedReturnToMooniswapPoolToken( fromToken, toToken, amount, parts, FLAG_DISABLE_MOONISWAP_POOL_TOKEN ); return (returnAmount, 0, distribution); } } return super.getExpectedReturnWithGas( fromToken, toToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function _getExpectedReturnFromMooniswapPoolToken( IERC20 poolToken, IERC20 toToken, uint256 amount, uint256 parts, uint256 flags ) private view returns( uint256 returnAmount, uint256[] memory distribution ) { distribution = new uint256[](DEXES_COUNT); PoolDetails memory details = _getPoolDetails(IMooniswap(address(poolToken))); for (uint i = 0; i < 2; i++) { uint256 exchangeAmount = amount .mul(details.tokens[i].reserve) .div(details.totalSupply); if (toToken.eq(details.tokens[i].token)) { returnAmount = returnAmount.add(exchangeAmount); continue; } (uint256 ret, ,uint256[] memory dist) = super.getExpectedReturnWithGas( details.tokens[i].token, toToken, exchangeAmount, parts, flags, 0 ); returnAmount = returnAmount.add(ret); for (uint j = 0; j < distribution.length; j++) { distribution[j] |= dist[j] << (i * 8); } } return (returnAmount, distribution); } function _getExpectedReturnToMooniswapPoolToken( IERC20 fromToken, IERC20 poolToken, uint256 amount, uint256 parts, uint256 flags ) private view returns( uint256 returnAmount, uint256[] memory distribution ) { distribution = new uint256[](DEXES_COUNT); PoolDetails memory details = _getPoolDetails(IMooniswap(address(poolToken))); // will overwritten to liquidity amounts uint256[2] memory amounts; amounts[0] = amount.div(2); amounts[1] = amount.sub(amounts[0]); uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < 2; i++) { if (fromToken.eq(details.tokens[i].token)) { continue; } (amounts[i], ,dist) = super.getExpectedReturnWithGas( fromToken, details.tokens[i].token, amounts[i], parts, flags, 0 ); for (uint j = 0; j < distribution.length; j++) { distribution[j] |= dist[j] << (i * 8); } } returnAmount = uint256(-1); for (uint i = 0; i < 2; i++) { returnAmount = Math.min( returnAmount, details.totalSupply.mul(amounts[i]).div(details.tokens[i].reserve) ); } return ( returnAmount, distribution ); } } contract OneSplitMooniswapToken is OneSplitBaseWrap, OneSplitMooniswapTokenBase { function _swap( IERC20 fromToken, IERC20 toToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { if (fromToken.eq(toToken)) { return; } if (!flags.check(FLAG_DISABLE_MOONISWAP_POOL_TOKEN)) { bool isPoolTokenFrom = mooniswapRegistry.isPool(address(fromToken)); bool isPoolTokenTo = mooniswapRegistry.isPool(address(toToken)); if (isPoolTokenFrom && isPoolTokenTo) { uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < distribution.length; i++) { dist[i] = distribution[i] & ((1 << 128) - 1); } uint256 ethBalanceBefore = ETH_ADDRESS.universalBalanceOf(address(this)); _swapFromMooniswapToken( fromToken, ETH_ADDRESS, amount, dist, FLAG_DISABLE_MOONISWAP_POOL_TOKEN ); for (uint i = 0; i < distribution.length; i++) { dist[i] = distribution[i] >> 128; } uint256 ethBalanceAfter = ETH_ADDRESS.universalBalanceOf(address(this)); return _swapToMooniswapToken( ETH_ADDRESS, toToken, ethBalanceAfter.sub(ethBalanceBefore), dist, FLAG_DISABLE_MOONISWAP_POOL_TOKEN ); } if (isPoolTokenFrom) { return _swapFromMooniswapToken( fromToken, toToken, amount, distribution, FLAG_DISABLE_MOONISWAP_POOL_TOKEN ); } if (isPoolTokenTo) { return _swapToMooniswapToken( fromToken, toToken, amount, distribution, FLAG_DISABLE_MOONISWAP_POOL_TOKEN ); } } return super._swap( fromToken, toToken, amount, distribution, flags ); } function _swapFromMooniswapToken( IERC20 poolToken, IERC20 toToken, uint256 amount, uint256[] memory distribution, uint256 flags ) private { IERC20[2] memory tokens = [ IMooniswap(address(poolToken)).tokens(0), IMooniswap(address(poolToken)).tokens(1) ]; IMooniswap(address(poolToken)).withdraw( amount, new uint256[](0) ); uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < 2; i++) { if (toToken.eq(tokens[i])) { continue; } for (uint j = 0; j < distribution.length; j++) { dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } super._swap( tokens[i], toToken, tokens[i].universalBalanceOf(address(this)), dist, flags ); } } function _swapToMooniswapToken( IERC20 fromToken, IERC20 poolToken, uint256 amount, uint256[] memory distribution, uint256 flags ) private { IERC20[2] memory tokens = [ IMooniswap(address(poolToken)).tokens(0), IMooniswap(address(poolToken)).tokens(1) ]; // will overwritten to liquidity amounts uint256[] memory amounts = new uint256[](2); amounts[0] = amount.div(2); amounts[1] = amount.sub(amounts[0]); uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < 2; i++) { if (fromToken.eq(tokens[i])) { continue; } for (uint j = 0; j < distribution.length; j++) { dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } super._swap( fromToken, tokens[i], amounts[i], dist, flags ); amounts[i] = tokens[i].universalBalanceOf(address(this)); tokens[i].universalApprove(address(poolToken), amounts[i]); } uint256 ethValue = (tokens[0].isETH() ? amounts[0] : 0) + (tokens[1].isETH() ? amounts[1] : 0); IMooniswap(address(poolToken)).deposit.value(ethValue)( amounts, 0 ); for (uint i = 0; i < 2; i++) { tokens[i].universalTransfer( msg.sender, tokens[i].universalBalanceOf(address(this)) ); } } } // File: contracts/OneSplit.sol pragma solidity ^0.5.0; contract OneSplitViewWrap is OneSplitViewWrapBase, OneSplitMStableView, OneSplitChaiView, OneSplitBdaiView, OneSplitAaveView, OneSplitFulcrumView, OneSplitCompoundView, OneSplitIearnView, OneSplitIdleView, OneSplitWethView, OneSplitDMMView, OneSplitMooniswapTokenView { IOneSplitView public oneSplitView; constructor(IOneSplitView _oneSplit) public { oneSplitView = _oneSplit; } function getExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) public view returns( uint256 returnAmount, uint256[] memory distribution ) { (returnAmount, , distribution) = getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, 0 ); } function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, // See constants in IOneSplit.sol uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { if (fromToken == destToken) { return (amount, 0, new uint256[](DEXES_COUNT)); } return super.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function _getExpectedReturnRespectingGasFloor( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) internal view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return oneSplitView.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } } contract OneSplitWrap is OneSplitBaseWrap, OneSplitMStable, OneSplitChai, OneSplitBdai, OneSplitAave, OneSplitFulcrum, OneSplitCompound, OneSplitIearn, OneSplitIdle, OneSplitWeth, OneSplitDMM, OneSplitMooniswapToken { IOneSplitView public oneSplitView; IOneSplit public oneSplit; constructor(IOneSplitView _oneSplitView, IOneSplit _oneSplit) public { oneSplitView = _oneSplitView; oneSplit = _oneSplit; } function() external payable { // solium-disable-next-line security/no-tx-origin require(msg.sender != tx.origin); } function getExpectedReturn( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags ) public view returns( uint256 returnAmount, uint256[] memory distribution ) { (returnAmount, , distribution) = getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, 0 ); } function getExpectedReturnWithGas( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 parts, uint256 flags, uint256 destTokenEthPriceTimesGasPrice ) public view returns( uint256 returnAmount, uint256 estimateGasAmount, uint256[] memory distribution ) { return oneSplitView.getExpectedReturnWithGas( fromToken, destToken, amount, parts, flags, destTokenEthPriceTimesGasPrice ); } function getExpectedReturnWithGasMulti( IERC20[] memory tokens, uint256 amount, uint256[] memory parts, uint256[] memory flags, uint256[] memory destTokenEthPriceTimesGasPrices ) public view returns( uint256[] memory returnAmounts, uint256 estimateGasAmount, uint256[] memory distribution ) { uint256[] memory dist; returnAmounts = new uint256[](tokens.length - 1); for (uint i = 1; i < tokens.length; i++) { if (tokens[i - 1] == tokens[i]) { returnAmounts[i - 1] = (i == 1) ? amount : returnAmounts[i - 2]; continue; } IERC20[] memory _tokens = tokens; ( returnAmounts[i - 1], amount, dist ) = getExpectedReturnWithGas( _tokens[i - 1], _tokens[i], (i == 1) ? amount : returnAmounts[i - 2], parts[i - 1], flags[i - 1], destTokenEthPriceTimesGasPrices[i - 1] ); estimateGasAmount = estimateGasAmount.add(amount); if (distribution.length == 0) { distribution = new uint256[](dist.length); } for (uint j = 0; j < distribution.length; j++) { distribution[j] = distribution[j].add(dist[j] << (8 * (i - 1))); } } } function swap( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256 minReturn, uint256[] memory distribution, uint256 flags ) public payable returns(uint256 returnAmount) { fromToken.universalTransferFrom(msg.sender, address(this), amount); uint256 confirmed = fromToken.universalBalanceOf(address(this)); _swap(fromToken, destToken, confirmed, distribution, flags); returnAmount = destToken.universalBalanceOf(address(this)); require(returnAmount >= minReturn, "OneSplit: actual return amount is less than minReturn"); destToken.universalTransfer(msg.sender, returnAmount); fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this))); } function swapMulti( IERC20[] memory tokens, uint256 amount, uint256 minReturn, uint256[] memory distribution, uint256[] memory flags ) public payable returns(uint256 returnAmount) { tokens[0].universalTransferFrom(msg.sender, address(this), amount); returnAmount = tokens[0].universalBalanceOf(address(this)); for (uint i = 1; i < tokens.length; i++) { if (tokens[i - 1] == tokens[i]) { continue; } uint256[] memory dist = new uint256[](distribution.length); for (uint j = 0; j < distribution.length; j++) { dist[j] = (distribution[j] >> (8 * (i - 1))) & 0xFF; } _swap( tokens[i - 1], tokens[i], returnAmount, dist, flags[i - 1] ); returnAmount = tokens[i].universalBalanceOf(address(this)); tokens[i - 1].universalTransfer(msg.sender, tokens[i - 1].universalBalanceOf(address(this))); } require(returnAmount >= minReturn, "OneSplit: actual return amount is less than minReturn"); tokens[tokens.length - 1].universalTransfer(msg.sender, returnAmount); } function _swapFloor( IERC20 fromToken, IERC20 destToken, uint256 amount, uint256[] memory distribution, uint256 flags ) internal { fromToken.universalApprove(address(oneSplit), amount); oneSplit.swap.value(fromToken.isETH() ? amount : 0)( fromToken, destToken, amount, 0, distribution, flags ); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"},{"internalType":"uint256","name":"destTokenEthPriceTimesGasPrice","type":"uint256"}],"name":"getExpectedReturnWithGas","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256","name":"estimateGasAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"flags","type":"uint256"}],"name":"swap","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b5060405162005070380380620050708339818101604052602081101561003557600080fd5b5051600080546001600160a01b039092166001600160a01b031990921691909117905561500880620000686000396000f3fe60806040526004361061003f5760003560e01c8063085e2c5b1461004e5780638373f265146100f8578063e2a7515e146101af578063fbe4ed9514610289575b3332141561004c57600080fd5b005b34801561005a57600080fd5b5061009d600480360360a081101561007157600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356102ba565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156100e35781810151838201526020016100cb565b50505050905001935050505060405180910390f35b34801561010457600080fd5b5061014d600480360360c081101561011b57600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a001356102dd565b6040518084815260200183815260200180602001828103825283818151815260200191508051906020019060200280838360005b83811015610199578181015183820152602001610181565b5050505090500194505050505060405180910390f35b610277600480360360c08110156101c557600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b81111561020457600080fd5b82018360208201111561021657600080fd5b803590602001918460208302840111600160201b8311171561023757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610434915050565b60408051918252519081900360200190f35b34801561029557600080fd5b5061029e61080d565b604080516001600160a01b039092168252519081900360200190f35b600060606102cd878787878760006102dd565b9199919850909650505050505050565b6000805460408051638373f26560e01b81526001600160a01b038a81166004830152898116602483015260448201899052606482018890526084820187905260a48201869052915184936060931691638373f2659160c48083019287929190829003018186803b15801561035057600080fd5b505afa158015610364573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052606081101561038d57600080fd5b81516020830151604080850180519151939592948301929184600160201b8211156103b757600080fd5b9083019060208201858111156103cc57600080fd5b82518660208202830111600160201b821117156103e857600080fd5b82525081516020918201928201910280838360005b838110156104155781810151838201526020016103fd565b5050505090500160405250505092509250925096509650969350505050565b6000856001600160a01b0316876001600160a01b03161415610457575083610803565b61045f614e34565b60405180610440016040528061081c8152602001610aa88152602001610af58152602001610e3b8152602001611104815260200161129a815260200161147681526020016116a681526020016118e08152602001611b1a8152602001611dc38152602001611feb81526020016123478152602001612560815260200161256c815260200161258781526020016125a281526020016125bd81526020016127f781526020016129848152602001612b788152602001612c248152602001612cfb8152602001612d9a8152602001612f8e8152602001612f9c8152602001612faa8152602001612fb88152602001612fd58152602001612feb815260200161300b8152602001613021815260200161303b81526020016130db81525090506022845111156105bc5760405162461bcd60e51b8152600401808060200182810382526042815260200180614f926042913960600191505060405180910390fd5b600080805b865181101561061a5760008782815181106105d857fe5b602002602001015111156106125761060c8782815181106105f557fe5b60200260200101518461314990919063ffffffff16565b92508091505b6001016105c1565b508161067a576106328a6001600160a01b03166131ac565b1561066f5760405133903480156108fc02916000818181858888f19350505050158015610663573d6000803e3d6000fd5b50349350505050610803565b879350505050610803565b6106956001600160a01b038b1633308b63ffffffff6131e516565b60006106b06001600160a01b038c163063ffffffff6132f716565b905060005b8751811015610753578781815181106106ca57fe5b6020026020010151600014156106df5761074b565b60006107178561070b8b85815181106106f457fe5b60200260200101518e6133a190919063ffffffff16565b9063ffffffff6133fa16565b9050838214156107245750815b80830392506107498d8d838b8a876022811061073c57fe5b602002015163ffffffff16565b505b6001016106b5565b5061076d6001600160a01b038b163063ffffffff6132f716565b9450878510156107ae5760405162461bcd60e51b8152600401808060200182810382526026815260200180614f0c6026913960400191505060405180910390fd5b6107c86001600160a01b038b16338763ffffffff61343c16565b506107fd336107e66001600160a01b038e163063ffffffff6132f716565b6001600160a01b038e16919063ffffffff61343c16565b50505050505b9695505050505050565b6000546001600160a01b031681565b8161082f6001600160a01b0386166131ac565b61096f57604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b15801561089157600080fd5b505afa1580156108a5573d6000803e3d6000fd5b505050506040513d60208110156108bb57600080fd5b505190506001600160a01b0381161561096d576108e86001600160a01b038716828463ffffffff6134ba16565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561093e57600080fd5b505af1158015610952573d6000803e3d6000fd5b505050506040513d602081101561096857600080fd5b505191505b505b610981846001600160a01b03166131ac565b610aa157604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156109e357600080fd5b505afa1580156109f7573d6000803e3d6000fd5b505050506040513d6020811015610a0d57600080fd5b505190506001600160a01b03811615610a9f57806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b158015610a6f57600080fd5b505af1158015610a83573d6000803e3d6000fd5b50505050506040513d6020811015610a9a57600080fd5b505191505b505b5050505050565b6040805162461bcd60e51b815260206004820152601a60248201527f5468697320736f75726365207761732064657072656361746564000000000000604482015290519081900360640190fd5b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b158015610b5f57600080fd5b505afa158015610b73573d6000803e3d6000fd5b505050506040513d6020811015610b8957600080fd5b505190506060736f0cd8c4f6f06eab664c7e3031909452b4b728616375e1cc82610bbb6001600160a01b0389166131ac565b610bc55787610bdb565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b610bed886001600160a01b03166131ac565b610bf75787610c0d565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b031681526020019250505060006040518083038186803b158015610c6c57600080fd5b505afa158015610c80573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015610ca957600080fd5b8101908080516040519392919084600160201b821115610cc857600080fd5b908301906020820185811115610cdd57600080fd5b82518660208202830111600160201b82111715610cf957600080fd5b82525081516020918201928201910280838360005b83811015610d26578181015183820152602001610d0e565b505050509050016040525050509050610d538285886001600160a01b03166134ba9092919063ffffffff16565b816001600160a01b031663f3898a97610d74886001600160a01b03166131ac565b610d7f576000610d81565b855b838760016040518563ffffffff1660e01b81526004018080602001848152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015610de0578181015183820152602001610dc8565b505050509050019450505050506020604051808303818588803b158015610e0657600080fd5b505af1158015610e1a573d6000803e3d6000fd5b50505050506040513d6020811015610e3157600080fd5b5050505050505050565b610e4d846001600160a01b03166131ac565b15610eb557600080516020614e608339815191526001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015610e9b57600080fd5b505af1158015610eaf573d6000803e3d6000fd5b50505050505b6000610ec9856001600160a01b03166131ac565b610ed35784610ee3565b600080516020614e608339815191525b9050610f136001600160a01b03821673794e6e91555438afc3ccf1c5076a74f42133d08d8563ffffffff6134ba16565b73794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f6610f3f6001600160a01b0388166131ac565b610f495786610f59565b600080516020614e608339815191525b85610f6c886001600160a01b03166131ac565b610f765787610f86565b600080516020614e608339815191525b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b158015610fe457600080fd5b505af1158015610ff8573d6000803e3d6000fd5b505050506040513d602081101561100e57600080fd5b5061102390506001600160a01b0385166131ac565b15610aa157604080516370a0823160e01b81523060048201529051600080516020614e6083398151915291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b15801561107b57600080fd5b505afa15801561108f573d6000803e3d6000fd5b505050506040513d60208110156110a557600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b1580156110e557600080fd5b505af11580156110f9573d6000803e3d6000fd5b505050505050505050565b60006001600160a01b038516600080516020614eec8339815191521461112b57600061112e565b60025b6001600160a01b038616600080516020614e8083398151915214611153576000611156565b60015b0160ff1690506000600080516020614eec8339815191526001600160a01b03861614611183576000611186565b60025b6001600160a01b038616600080516020614e80833981519152146111ab5760006111ae565b60015b0160ff16905081600f0b600014806111c9575080600f0b6000145b156111d5575050611294565b6112036001600160a01b03871673a2b47e3d5c44877cca798226b7b8118f9bfb7a568663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b505af115801561128d573d6000803e3d6000fd5b5050505050505b50505050565b60006001600160a01b03851673dac17f958d2ee523a2206206994597c13d831ec7146112c75760006112ca565b60035b6001600160a01b038616600080516020614eec833981519152146112ef5760006112f2565b60025b6001600160a01b038716600080516020614e808339815191521461131757600061131a565b60015b010160ff169050600073dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0316856001600160a01b03161461135757600061135a565b60035b6001600160a01b038616600080516020614eec8339815191521461137f576000611382565b60025b6001600160a01b038716600080516020614e80833981519152146113a75760006113aa565b60015b010160ff16905081600f0b600014806113c6575080600f0b6000145b156113d2575050611294565b6114006001600160a01b0387167352ea46506b9cc5ef470c5bf89f17dc28bb35d85c8663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b60006001600160a01b0385166e085d4780b73119b644ae5ecd22b3761461149e5760006114a1565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec7146114cc5760006114cf565b60035b6001600160a01b038716600080516020614eec833981519152146114f45760006114f7565b60025b6001600160a01b038816600080516020614e808339815191521461151c57600061151f565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b03161461155857600061155b565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611586576000611589565b60035b6001600160a01b038716600080516020614eec833981519152146115ae5760006115b1565b60025b6001600160a01b038816600080516020614e80833981519152146115d65760006115d9565b60015b01010160ff16905081600f0b600014806115f6575080600f0b6000145b15611602575050611294565b6116306001600160a01b0387167345f783cce6b7ff23b2ab2d70e416cdb7d6055f518663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b60006001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c53146116d35760006116d6565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611701576000611704565b60035b6001600160a01b038716600080516020614eec8339815191521461172957600061172c565b60025b6001600160a01b038816600080516020614e8083398151915214611751576000611754565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b031614611792576000611795565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec7146117c05760006117c3565b60035b6001600160a01b038716600080516020614eec833981519152146117e85760006117eb565b60025b6001600160a01b038816600080516020614e8083398151915214611810576000611813565b60015b01010160ff16905081600f0b60001480611830575080600f0b6000145b1561183c575050611294565b61186a6001600160a01b0387167379a8c46dea5ada233abaffd40f3a0a2b1e5a4f278663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b60006001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f511461190d576000611910565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec71461193b57600061193e565b60035b6001600160a01b038716600080516020614eec83398151915214611963576000611966565b60025b6001600160a01b038816600080516020614e808339815191521461198b57600061198e565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b0316146119cc5760006119cf565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec7146119fa5760006119fd565b60035b6001600160a01b038716600080516020614eec83398151915214611a22576000611a25565b60025b6001600160a01b038816600080516020614e8083398151915214611a4a576000611a4d565b60015b01010160ff16905081600f0b60001480611a6a575080600f0b6000145b15611a76575050611294565b611aa46001600160a01b03871673a5407eae9ba41422680e2e00537571bcc53efbfd8663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b611b2c846001600160a01b03166131ac565b611c7357604080516332a5d5bf60e01b81526001600160a01b0386166004820152905160009173f451dbd7ba14bfa7b1b78a766d3ed438f79ee1d1916332a5d5bf91602480820192602092909190829003018186803b158015611b8e57600080fd5b505afa158015611ba2573d6000803e3d6000fd5b505050506040513d6020811015611bb857600080fd5b50519050611bd66001600160a01b038616828563ffffffff6134ba16565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015611c1c57600080fd5b505af1158015611c30573d6000803e3d6000fd5b505050506040513d6020811015611c4657600080fd5b50611c6d90508185611c676001600160a01b0383163063ffffffff6132f716565b8561081c565b50611294565b611c85836001600160a01b03166131ac565b61129457604080516332a5d5bf60e01b81526001600160a01b0385166004820152905160009173f451dbd7ba14bfa7b1b78a766d3ed438f79ee1d1916332a5d5bf91602480820192602092909190829003018186803b158015611ce757600080fd5b505afa158015611cfb573d6000803e3d6000fd5b505050506040513d6020811015611d1157600080fd5b50519050611d218582858561081c565b6001600160a01b03811663db006a75611d40823063ffffffff6132f716565b6040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015611d7657600080fd5b505af1158015611d8a573d6000803e3d6000fd5b505050506040513d6020811015611da057600080fd5b50611dbc90506001600160a01b0385163063ffffffff6132f716565b5050611294565b6001600160a01b038416600080516020614e808339815191521415611eb857611e106001600160a01b0385167306af07097c9eeb7fd685c692751d5c66db49c2158463ffffffff6134ba16565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b158015611e6957600080fd5b505af1158015611e7d573d6000803e3d6000fd5b50611eb392507306af07097c9eeb7fd685c692751d5c66db49c2159150859050611ead823063ffffffff6132f716565b8461081c565b611294565b6001600160a01b038316600080516020614e80833981519152141561129457611ef7847306af07097c9eeb7fd685c692751d5c66db49c215848461081c565b604080516370a0823160e01b8152306004820181905291517306af07097c9eeb7fd685c692751d5c66db49c2159263ef693bed92909184916370a08231916024808301926020929190829003018186803b158015611f5457600080fd5b505afa158015611f68573d6000803e3d6000fd5b505050506040513d6020811015611f7e57600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b039093166004840152602483019190915251604480830192600092919082900301818387803b158015611fce57600080fd5b505af1158015611fe2573d6000803e3d6000fd5b50505050611294565b611ffd846001600160a01b03166131ac565b6121c35760408051635f5418f360e01b81526001600160a01b0386166004820152905160009173ed8b133b7b88366e01bb9e38305ab11c2652149491635f5418f391602480820192602092909190829003018186803b15801561205f57600080fd5b505afa158015612073573d6000803e3d6000fd5b505050506040513d602081101561208957600080fd5b50516040805163797a759360e11b815290519192506121209173398ec7346dcd622edc5ae82352f02be94c62d1199163f2f4eb26916004808301926020929190829003018186803b1580156120dd57600080fd5b505afa1580156120f1573d6000803e3d6000fd5b505050506040513d602081101561210757600080fd5b50516001600160a01b038716908563ffffffff6134ba16565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b15801561218a57600080fd5b505af115801561219e573d6000803e3d6000fd5b50505050611c6d8185611c6730856001600160a01b03166132f790919063ffffffff16565b6121d5836001600160a01b03166131ac565b6112945760408051635f5418f360e01b81526001600160a01b0385166004820152905160009173ed8b133b7b88366e01bb9e38305ab11c2652149491635f5418f391602480820192602092909190829003018186803b15801561223757600080fd5b505afa15801561224b573d6000803e3d6000fd5b505050506040513d602081101561226157600080fd5b505190506122718582858561081c565b604080516370a0823160e01b815230600482015290516001600160a01b0383169163db006a759183916370a08231916024808301926020929190829003018186803b1580156122bf57600080fd5b505afa1580156122d3573d6000803e3d6000fd5b505050506040513d60208110156122e957600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561232957600080fd5b505af115801561233d573d6000803e3d6000fd5b5050505050611294565b600073c12a7e093832e2d2267df225baca60bd2b74c65f63901754d76123756001600160a01b0388166131ac565b61237f5786612382565b60005b612394876001600160a01b03166131ac565b61239e57866123a1565b60005b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b031681526020019250505060206040518083038186803b15801561240057600080fd5b505afa158015612414573d6000803e3d6000fd5b505050506040513d602081101561242a57600080fd5b505190506124486001600160a01b038616828563ffffffff6134ba16565b806001600160a01b031663d5bcb9b5612469876001600160a01b03166131ac565b612474576000612476565b845b612488886001600160a01b03166131ac565b6124925787612495565b60005b6124a7886001600160a01b03166131ac565b6124b157876124b4565b60005b604080516001600160e01b031960e087901b1681526001600160a01b0393841660048201529190921660248201526044810188905260006064820152734d37f28d2db99e8d35a6c725a5f1749a085850a36084820152905160a480830192602092919082900301818588803b15801561252c57600080fd5b505af1158015612540573d6000803e3d6000fd5b50505050506040513d602081101561255757600080fd5b50505050505050565b610aa1848484846135b3565b61129484600080516020614e60833981519152858585613965565b61129484600080516020614e80833981519152858585613965565b61129484600080516020614eec833981519152858585613965565b60006001600160a01b038516738e870d67f660d95d5be530380d0ec0bd388289e1146125ea5760006125ed565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec71461261857600061261b565b60035b6001600160a01b038716600080516020614eec83398151915214612640576000612643565b60025b6001600160a01b038816600080516020614e808339815191521461266857600061266b565b60015b01010160ff1690506000738e870d67f660d95d5be530380d0ec0bd388289e16001600160a01b0316856001600160a01b0316146126a95760006126ac565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec7146126d75760006126da565b60035b6001600160a01b038716600080516020614eec833981519152146126ff576000612702565b60025b6001600160a01b038816600080516020614e808339815191521461272757600061272a565b60015b01010160ff16905081600f0b60001480612747575080600f0b6000145b15612753575050611294565b6127816001600160a01b0387167306364f10b501e868329afbc005b3492902d6c7638663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517306364f10b501e868329afbc005b3492902d6c7639263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b60006001600160a01b038516732260fac5e5542a773aa44fbcfedf7c193bc2c59914612824576000612827565b60025b6001600160a01b03861673eb4c2781e4eba804ce9a9803c67d0893436bb27d14612852576000612855565b60015b0160ff1690506000732260fac5e5542a773aa44fbcfedf7c193bc2c5996001600160a01b0386161461288857600061288b565b60025b6001600160a01b03861673eb4c2781e4eba804ce9a9803c67d0893436bb27d146128b65760006128b9565b60015b0160ff16905081600f0b600014806128d4575080600f0b6000145b156128e0575050611294565b61290e6001600160a01b0387167393054188d876f558f4a66b2ef1d97d16edf0895b8663ffffffff6134ba16565b60408051630f7c084960e21b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517393054188d876f558f4a66b2ef1d97d16edf0895b92633df02124926084808201939182900301818387803b15801561127957600080fd5b60006001600160a01b038516730316eb71485b0ab14103307bf65a021042c6d380146129b15760006129b4565b60035b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c599146129df5760006129e2565b60025b6001600160a01b038716731bbe271d15bb64df0bc6cd28df9ff322f2ebd84714612a0d576000612a10565b60015b010160ff1690506000730316eb71485b0ab14103307bf65a021042c6d3806001600160a01b0316856001600160a01b031614612a4d576000612a50565b60035b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c59914612a7b576000612a7e565b60025b6001600160a01b038716731bbe271d15bb64df0bc6cd28df9ff322f2ebd84714612aa9576000612aac565b60015b010160ff16905081600f0b60001480612ac8575080600f0b6000145b15612ad4575050611294565b612b026001600160a01b038716739726e9314ef1b96e45f40056bed61a088897313e8663ffffffff6134ba16565b60408051630f7c084960e21b8152600019808501600f90810b810b6004840152908401810b900b6024820152604481018690526000606482018190529151739726e9314ef1b96e45f40056bed61a088897313e92633df02124926084808201939182900301818387803b15801561127957600080fd5b612ba66001600160a01b0385167303ef3f37856bd08eb47e2de7abc4ddd2c19b60f28463ffffffff6134ba16565b60408051630df791e560e41b81526001600160a01b038681166004830152851660248201526044810184905290517303ef3f37856bd08eb47e2de7abc4ddd2c19b60f29163df791e5091606480830192600092919082900301818387803b158015612c1057600080fd5b505af1158015610e31573d6000803e3d6000fd5b612c526001600160a01b03851673a8253a440be331dc4a7395b73948cca6f19dc97d8463ffffffff6134ba16565b604080516303ff4c0160e31b81526001600160a01b0386811660048301528516602482015260448101849052600060648201819052603242016084830152915173a8253a440be331dc4a7395b73948cca6f19dc97d92631ffa60089260a480820193602093909283900390910190829087803b158015612cd157600080fd5b505af1158015612ce5573d6000803e3d6000fd5b505050506040513d6020811015610a9f57600080fd5b612d296001600160a01b03851673e2f2a5c287993345a840db3b0845fbc70f5935a58463ffffffff6134ba16565b60408051631ba0488760e21b81526001600160a01b0386811660048301528516602482015260448101849052306064820152905173e2f2a5c287993345a840db3b0845fbc70f5935a591636e81221c9160848083019260209291908290030181600087803b158015612cd157600080fd5b60006001600160a01b03851673fe18be6b3bd88a2d2a7f928d00292e7a9963cfc614612dc7576000612dca565b60035b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c59914612df5576000612df8565b60025b6001600160a01b03871673eb4c2781e4eba804ce9a9803c67d0893436bb27d14612e23576000612e26565b60015b010160ff169050600073fe18be6b3bd88a2d2a7f928d00292e7a9963cfc66001600160a01b0316856001600160a01b031614612e63576000612e66565b60035b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c59914612e91576000612e94565b60025b6001600160a01b03871673eb4c2781e4eba804ce9a9803c67d0893436bb27d14612ebf576000612ec2565b60015b010160ff16905081600f0b60001480612ede575080600f0b6000145b15612eea575050611294565b612f186001600160a01b038716737fc77b5c7614e1533320ea6ddc2eb61fa00a97148663ffffffff6134ba16565b60408051630f7c084960e21b8152600019808501600f90810b810b6004840152908401810b900b6024820152604481018690526000606482018190529151737fc77b5c7614e1533320ea6ddc2eb61fa00a971492633df02124926084808201939182900301818387803b15801561127957600080fd5b61129484848484600061397c565b61129484848484600161397c565b61129484848484600261397c565b611294848484846001685a434ecd46efdcc7c760b11b0319613ded565b61129484848484600161543360e81b0319613ded565b6112948484848460016b2c2466af65a2f7eba2a7a463609a1b0319613ded565b6112948484848461301c8989614611565b613ded565b61302e8460008484612347565b6112946000844784612347565b61305584600080516020614e808339815191528484612347565b604080516370a0823160e01b8152306004820152905161129491600080516020614e8083398151915291869183916370a0823191602480820192602092909190829003018186803b1580156130a957600080fd5b505afa1580156130bd573d6000803e3d6000fd5b505050506040513d60208110156130d357600080fd5b505184612347565b6130f584600080516020614eec8339815191528484612347565b604080516370a0823160e01b8152306004820152905161129491600080516020614eec83398151915291869183916370a0823191602480820192602092909190829003018186803b1580156130a957600080fd5b6000828201838110156131a3576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60006001600160a01b03821615806131a657506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1492915050565b806131ef57611294565b6131f8846131ac565b156132dc576001600160a01b038316331480156132155750803410155b6132505760405162461bcd60e51b815260040180806020018281038252602b815260200180614ea0602b913960400191505060405180910390fd5b6001600160a01b0382163014613298576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015613296573d6000803e3d6000fd5b505b80341115611eb357336108fc6132b4348463ffffffff61488c16565b6040518115909202916000818181858888f19350505050158015611c6d573d6000803e3d6000fd5b6112946001600160a01b03851684848463ffffffff6148ce16565b6000613302836131ac565b1561331857506001600160a01b038116316131a6565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561336e57600080fd5b505afa158015613382573d6000803e3d6000fd5b505050506040513d602081101561339857600080fd5b505190506131a6565b6000826133b0575060006131a6565b828202828482816133bd57fe5b04146131a35760405162461bcd60e51b8152600401808060200182810382526021815260200180614ecb6021913960400191505060405180910390fd5b60006131a383836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614928565b60008161344b575060016134b3565b613454846131ac565b15613495576040516001600160a01b0384169083156108fc029084906000818181858888f1935050505015801561348f573d6000803e3d6000fd5b506134b3565b6134af6001600160a01b038516848463ffffffff6149ca16565b5060015b9392505050565b6134c3836131ac565b6135ae57806134ec576134e76001600160a01b03841683600063ffffffff614a1c16565b6135ae565b60408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561353d57600080fd5b505afa158015613551573d6000803e3d6000fd5b505050506040513d602081101561356757600080fd5b5051905081811015611294578015613594576135946001600160a01b03851684600063ffffffff614a1c16565b6112946001600160a01b038516848463ffffffff614a1c16565b505050565b60006135c7856001600160a01b03166131ac565b1561362f57600080516020614e608339815191526001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561361557600080fd5b505af1158015613629573d6000803e3d6000fd5b50505050505b6000613643866001600160a01b03166131ac565b61364d578561365d565b600080516020614e608339815191525b90506000613673866001600160a01b03166131ac565b61367d578561368d565b600080516020614e608339815191525b6040805163e6a4390560e01b81526001600160a01b038581166004830152831660248201529051919250600091735c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f9163e6a43905916044808301926020929190829003018186803b1580156136f557600080fd5b505afa158015613709573d6000803e3d6000fd5b505050506040513d602081101561371f57600080fd5b5051905061373e6001600160a01b03821684848963ffffffff614b2f16565b935061375a6001600160a01b038416828863ffffffff61343c16565b50816001600160a01b0316836001600160a01b031610156137f3576040805163022c0d9f60e01b815260006004820181905260248201879052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b1580156137d657600080fd5b505af11580156137ea573d6000803e3d6000fd5b5050505061386d565b6040805163022c0d9f60e01b815260048101869052600060248201819052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b15801561385457600080fd5b505af1158015613868573d6000803e3d6000fd5b505050505b61387f876001600160a01b03166131ac565b1561395a57604080516370a0823160e01b81523060048201529051600080516020614e6083398151915291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156138d757600080fd5b505afa1580156138eb573d6000803e3d6000fd5b505050506040513d602081101561390157600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561394157600080fd5b505af1158015613955573d6000803e3d6000fd5b505050505b505050949350505050565b610a9f8484613976888887876135b3565b846135b3565b60607365e67cbc342712df67494acefc06fe951ee9398263bfdbfc436139aa6001600160a01b0389166131ac565b6139b457876139c4565b600080516020614e608339815191525b6139d6886001600160a01b03166131ac565b6139e057876139f0565b600080516020614e608339815191525b856001016040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160a01b03166001600160a01b03168152602001828152602001935050505060006040518083038186803b158015613a5a57600080fd5b505afa158015613a6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015613a9757600080fd5b8101908080516040519392919084600160201b821115613ab657600080fd5b908301906020820185811115613acb57600080fd5b82518660208202830111600160201b82111715613ae757600080fd5b82525081516020918201928201910280838360005b83811015613b14578181015183820152602001613afc565b505050509050016040525050509050613b35866001600160a01b03166131ac565b15613b9d57600080516020614e608339815191526001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b158015613b8357600080fd5b505af1158015613b97573d6000803e3d6000fd5b50505050505b613bf7818381518110613bac57fe5b602002602001015185613bc7896001600160a01b03166131ac565b613bd15788613be1565b600080516020614e608339815191525b6001600160a01b0316919063ffffffff6134ba16565b808281518110613c0357fe5b60200260200101516001600160a01b0316638201aa3f613c2b886001600160a01b03166131ac565b613c355787613c45565b600080516020614e608339815191525b86613c58896001600160a01b03166131ac565b613c625788613c72565b600080516020614e608339815191525b60006000196040518663ffffffff1660e01b815260040180866001600160a01b03166001600160a01b03168152602001858152602001846001600160a01b03166001600160a01b03168152602001838152602001828152602001955050505050506040805180830381600087803b158015613cec57600080fd5b505af1158015613d00573d6000803e3d6000fd5b505050506040513d6040811015613d1657600080fd5b50613d2b90506001600160a01b0386166131ac565b15610a9f57604080516370a0823160e01b81523060048201529051600080516020614e6083398151915291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015613d8357600080fd5b505afa158015613d97573d6000803e3d6000fd5b505050506040513d6020811015613dad57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561127957600080fd5b604080516001808252818301909252849160609190602080830190803883390190505090508281600081518110613e2057fe5b602002602001018181525050613e3e876001600160a01b03166131ac565b61423957606073a1c0fa73c39cfbcc11ec9eb1afc665aba9996e2c6001600160a01b03166361e597f9896001856000604051908082528060200260200182016040528015613e96578160200160208202803883390190505b506040518563ffffffff1660e01b815260040180856001600160a01b03166001600160a01b03168152602001846003811115613ece57fe5b60ff1681526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015613f16578181015183820152602001613efe565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015613f55578181015183820152602001613f3d565b50505050905001965050505050505060006040518083038186803b158015613f7c57600080fd5b505afa158015613f90573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015613fb957600080fd5b8101908080516040519392919084600160201b821115613fd857600080fd5b908301906020820185811115613fed57600080fd5b8251600160201b81118282018810171561400657600080fd5b82525081516020918201929091019080838360005b8381101561403357818101518382015260200161401b565b50505050905090810190601f1680156140605780820380516001836020036101000a031916815260200191505b50604052509192506140999150506001600160a01b038916739aab3f75489902f3a48495025729a0af77d4b11e8863ffffffff6134ba16565b739aab3f75489902f3a48495025729a0af77d4b11e6001600160a01b031663ae591d54898573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee306000196000734d37f28d2db99e8d35a6c725a5f1749a085850a3600a8a6040518a63ffffffff1660e01b8152600401808a6001600160a01b03166001600160a01b03168152602001898152602001886001600160a01b03166001600160a01b03168152602001876001600160a01b03166001600160a01b03168152602001868152602001858152602001846001600160a01b03166001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b838110156141b557818101518382015260200161419d565b50505050905090810190601f1680156141e25780820380516001836020036101000a031916815260200191505b509a5050505050505050505050602060405180830381600087803b15801561420957600080fd5b505af115801561421d573d6000803e3d6000fd5b505050506040513d602081101561423357600080fd5b50519250505b61424b866001600160a01b03166131ac565b61255757606073a1c0fa73c39cfbcc11ec9eb1afc665aba9996e2c6001600160a01b03166381efcbdd8860018560006040519080825280602002602001820160405280156142a3578160200160208202803883390190505b506040518563ffffffff1660e01b815260040180856001600160a01b03166001600160a01b031681526020018460038111156142db57fe5b60ff1681526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561432357818101518382015260200161430b565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561436257818101518382015260200161434a565b50505050905001965050505050505060006040518083038186803b15801561438957600080fd5b505afa15801561439d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156143c657600080fd5b8101908080516040519392919084600160201b8211156143e557600080fd5b9083019060208201858111156143fa57600080fd5b8251600160201b81118282018810171561441357600080fd5b82525081516020918201929091019080838360005b83811015614440578181015183820152602001614428565b50505050905090810190601f16801561446d5780820380516001836020036101000a031916815260200191505b506040525050509050739aab3f75489902f3a48495025729a0af77d4b11e6001600160a01b031663ae591d548473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee868b306000196000734d37f28d2db99e8d35a6c725a5f1749a085850a3600a8b6040518b63ffffffff1660e01b8152600401808a6001600160a01b03166001600160a01b03168152602001898152602001886001600160a01b03166001600160a01b03168152602001876001600160a01b03166001600160a01b03168152602001868152602001858152602001846001600160a01b03166001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561459357818101518382015260200161457b565b50505050905090810190601f1680156145c05780820380516001836020036101000a031916815260200191505b509a50505050505050505050506020604051808303818588803b1580156145e657600080fd5b505af11580156145fa573d6000803e3d6000fd5b50505050506040513d602081101561128d57600080fd5b6000614625836001600160a01b03166131ac565b158015614641575061463f826001600160a01b03166131ac565b155b1561464e575060006131a6565b606073c8fb12402cb16970f3c5f4b48ff68eb9d1289301633d3dc52c61467c6001600160a01b0387166131ac565b6146865785614688565b845b6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060006040518083038186803b1580156146ce57600080fd5b505afa1580156146e2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561470b57600080fd5b8101908080516040519392919084600160201b82111561472a57600080fd5b90830190602082018581111561473f57600080fd5b82518660208202830111600160201b8211171561475b57600080fd5b82525081516020918201928201910280838360005b83811015614788578181015183820152602001614770565b50505050905001604052505050905060008090505b81518110156148815760f88282815181106147b457fe5b602002602001015160001c901c60bb141580156147f757508181815181106147d857fe5b60200260200101516001685a434ecd46efdcc7c760b11b031960001b14155b8015614822575081818151811061480a57fe5b6020026020010151600161543360e81b031960001b14155b8015614857575081818151811061483557fe5b602002602001015160016b2c2466af65a2f7eba2a7a463609a1b031960001b14155b156148795781818151811061486857fe5b6020026020010151925050506131a6565b60010161479d565b506000949350505050565b60006131a383836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250614be6565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611294908590614c40565b600081836149b45760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614979578181015183820152602001614961565b50505050905090810190601f1680156149a65780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816149c057fe5b0495945050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526135ae908490614c40565b801580614aa2575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015614a7457600080fd5b505afa158015614a88573d6000803e3d6000fd5b505050506040513d6020811015614a9e57600080fd5b5051155b614add5760405162461bcd60e51b8152600401808060200182810382526036815260200180614f5c6036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526135ae908490614c40565b600080614b4b6001600160a01b0386168763ffffffff6132f716565b90506000614b686001600160a01b0386168863ffffffff6132f716565b90506000614b7e856103e563ffffffff6133a116565b90506000614b92828463ffffffff6133a116565b90506000614bb883614bac876103e863ffffffff6133a116565b9063ffffffff61314916565b90508015614bd557614bd0828263ffffffff6133fa16565b614bd8565b60005b9a9950505050505050505050565b60008184841115614c385760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315614979578181015183820152602001614961565b505050900390565b614c52826001600160a01b0316614df8565b614ca3576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310614ce15780518252601f199092019160209182019101614cc2565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614d43576040519150601f19603f3d011682016040523d82523d6000602084013e614d48565b606091505b509150915081614d9f576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b80511561129457808060200190516020811015614dbb57600080fd5b50516112945760405162461bcd60e51b815260040180806020018281038252602a815260200180614f32602a913960400191505060405180910390fd5b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590614e2c57508115155b949350505050565b6040518061044001604052806022905b614e5d815260200190600190039081614e445790505090565bfefe000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f57726f6e6720757365616765206f66204554482e756e6976657273616c5472616e7366657246726f6d2829536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb484f6e6553706c69743a2052657475726e20616d6f756e7420776173206e6f7420656e6f7567685361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820de530c3963242374be6d69dd06ead3f3520b8b1e26334841b7db5b7f12b8767964736f6c63430005110032000000000000000000000000b97274ad8102cd447578338e66a529611c9609de
Deployed Bytecode
0x60806040526004361061003f5760003560e01c8063085e2c5b1461004e5780638373f265146100f8578063e2a7515e146101af578063fbe4ed9514610289575b3332141561004c57600080fd5b005b34801561005a57600080fd5b5061009d600480360360a081101561007157600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356102ba565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156100e35781810151838201526020016100cb565b50505050905001935050505060405180910390f35b34801561010457600080fd5b5061014d600480360360c081101561011b57600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a001356102dd565b6040518084815260200183815260200180602001828103825283818151815260200191508051906020019060200280838360005b83811015610199578181015183820152602001610181565b5050505090500194505050505060405180910390f35b610277600480360360c08110156101c557600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b81111561020457600080fd5b82018360208201111561021657600080fd5b803590602001918460208302840111600160201b8311171561023757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610434915050565b60408051918252519081900360200190f35b34801561029557600080fd5b5061029e61080d565b604080516001600160a01b039092168252519081900360200190f35b600060606102cd878787878760006102dd565b9199919850909650505050505050565b6000805460408051638373f26560e01b81526001600160a01b038a81166004830152898116602483015260448201899052606482018890526084820187905260a48201869052915184936060931691638373f2659160c48083019287929190829003018186803b15801561035057600080fd5b505afa158015610364573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052606081101561038d57600080fd5b81516020830151604080850180519151939592948301929184600160201b8211156103b757600080fd5b9083019060208201858111156103cc57600080fd5b82518660208202830111600160201b821117156103e857600080fd5b82525081516020918201928201910280838360005b838110156104155781810151838201526020016103fd565b5050505090500160405250505092509250925096509650969350505050565b6000856001600160a01b0316876001600160a01b03161415610457575083610803565b61045f614e34565b60405180610440016040528061081c8152602001610aa88152602001610af58152602001610e3b8152602001611104815260200161129a815260200161147681526020016116a681526020016118e08152602001611b1a8152602001611dc38152602001611feb81526020016123478152602001612560815260200161256c815260200161258781526020016125a281526020016125bd81526020016127f781526020016129848152602001612b788152602001612c248152602001612cfb8152602001612d9a8152602001612f8e8152602001612f9c8152602001612faa8152602001612fb88152602001612fd58152602001612feb815260200161300b8152602001613021815260200161303b81526020016130db81525090506022845111156105bc5760405162461bcd60e51b8152600401808060200182810382526042815260200180614f926042913960600191505060405180910390fd5b600080805b865181101561061a5760008782815181106105d857fe5b602002602001015111156106125761060c8782815181106105f557fe5b60200260200101518461314990919063ffffffff16565b92508091505b6001016105c1565b508161067a576106328a6001600160a01b03166131ac565b1561066f5760405133903480156108fc02916000818181858888f19350505050158015610663573d6000803e3d6000fd5b50349350505050610803565b879350505050610803565b6106956001600160a01b038b1633308b63ffffffff6131e516565b60006106b06001600160a01b038c163063ffffffff6132f716565b905060005b8751811015610753578781815181106106ca57fe5b6020026020010151600014156106df5761074b565b60006107178561070b8b85815181106106f457fe5b60200260200101518e6133a190919063ffffffff16565b9063ffffffff6133fa16565b9050838214156107245750815b80830392506107498d8d838b8a876022811061073c57fe5b602002015163ffffffff16565b505b6001016106b5565b5061076d6001600160a01b038b163063ffffffff6132f716565b9450878510156107ae5760405162461bcd60e51b8152600401808060200182810382526026815260200180614f0c6026913960400191505060405180910390fd5b6107c86001600160a01b038b16338763ffffffff61343c16565b506107fd336107e66001600160a01b038e163063ffffffff6132f716565b6001600160a01b038e16919063ffffffff61343c16565b50505050505b9695505050505050565b6000546001600160a01b031681565b8161082f6001600160a01b0386166131ac565b61096f57604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b15801561089157600080fd5b505afa1580156108a5573d6000803e3d6000fd5b505050506040513d60208110156108bb57600080fd5b505190506001600160a01b0381161561096d576108e86001600160a01b038716828463ffffffff6134ba16565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561093e57600080fd5b505af1158015610952573d6000803e3d6000fd5b505050506040513d602081101561096857600080fd5b505191505b505b610981846001600160a01b03166131ac565b610aa157604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156109e357600080fd5b505afa1580156109f7573d6000803e3d6000fd5b505050506040513d6020811015610a0d57600080fd5b505190506001600160a01b03811615610a9f57806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b158015610a6f57600080fd5b505af1158015610a83573d6000803e3d6000fd5b50505050506040513d6020811015610a9a57600080fd5b505191505b505b5050505050565b6040805162461bcd60e51b815260206004820152601a60248201527f5468697320736f75726365207761732064657072656361746564000000000000604482015290519081900360640190fd5b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b158015610b5f57600080fd5b505afa158015610b73573d6000803e3d6000fd5b505050506040513d6020811015610b8957600080fd5b505190506060736f0cd8c4f6f06eab664c7e3031909452b4b728616375e1cc82610bbb6001600160a01b0389166131ac565b610bc55787610bdb565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b610bed886001600160a01b03166131ac565b610bf75787610c0d565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b031681526020019250505060006040518083038186803b158015610c6c57600080fd5b505afa158015610c80573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015610ca957600080fd5b8101908080516040519392919084600160201b821115610cc857600080fd5b908301906020820185811115610cdd57600080fd5b82518660208202830111600160201b82111715610cf957600080fd5b82525081516020918201928201910280838360005b83811015610d26578181015183820152602001610d0e565b505050509050016040525050509050610d538285886001600160a01b03166134ba9092919063ffffffff16565b816001600160a01b031663f3898a97610d74886001600160a01b03166131ac565b610d7f576000610d81565b855b838760016040518563ffffffff1660e01b81526004018080602001848152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015610de0578181015183820152602001610dc8565b505050509050019450505050506020604051808303818588803b158015610e0657600080fd5b505af1158015610e1a573d6000803e3d6000fd5b50505050506040513d6020811015610e3157600080fd5b5050505050505050565b610e4d846001600160a01b03166131ac565b15610eb557600080516020614e608339815191526001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015610e9b57600080fd5b505af1158015610eaf573d6000803e3d6000fd5b50505050505b6000610ec9856001600160a01b03166131ac565b610ed35784610ee3565b600080516020614e608339815191525b9050610f136001600160a01b03821673794e6e91555438afc3ccf1c5076a74f42133d08d8563ffffffff6134ba16565b73794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f6610f3f6001600160a01b0388166131ac565b610f495786610f59565b600080516020614e608339815191525b85610f6c886001600160a01b03166131ac565b610f765787610f86565b600080516020614e608339815191525b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b158015610fe457600080fd5b505af1158015610ff8573d6000803e3d6000fd5b505050506040513d602081101561100e57600080fd5b5061102390506001600160a01b0385166131ac565b15610aa157604080516370a0823160e01b81523060048201529051600080516020614e6083398151915291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b15801561107b57600080fd5b505afa15801561108f573d6000803e3d6000fd5b505050506040513d60208110156110a557600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b1580156110e557600080fd5b505af11580156110f9573d6000803e3d6000fd5b505050505050505050565b60006001600160a01b038516600080516020614eec8339815191521461112b57600061112e565b60025b6001600160a01b038616600080516020614e8083398151915214611153576000611156565b60015b0160ff1690506000600080516020614eec8339815191526001600160a01b03861614611183576000611186565b60025b6001600160a01b038616600080516020614e80833981519152146111ab5760006111ae565b60015b0160ff16905081600f0b600014806111c9575080600f0b6000145b156111d5575050611294565b6112036001600160a01b03871673a2b47e3d5c44877cca798226b7b8118f9bfb7a568663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b505af115801561128d573d6000803e3d6000fd5b5050505050505b50505050565b60006001600160a01b03851673dac17f958d2ee523a2206206994597c13d831ec7146112c75760006112ca565b60035b6001600160a01b038616600080516020614eec833981519152146112ef5760006112f2565b60025b6001600160a01b038716600080516020614e808339815191521461131757600061131a565b60015b010160ff169050600073dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0316856001600160a01b03161461135757600061135a565b60035b6001600160a01b038616600080516020614eec8339815191521461137f576000611382565b60025b6001600160a01b038716600080516020614e80833981519152146113a75760006113aa565b60015b010160ff16905081600f0b600014806113c6575080600f0b6000145b156113d2575050611294565b6114006001600160a01b0387167352ea46506b9cc5ef470c5bf89f17dc28bb35d85c8663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b60006001600160a01b0385166e085d4780b73119b644ae5ecd22b3761461149e5760006114a1565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec7146114cc5760006114cf565b60035b6001600160a01b038716600080516020614eec833981519152146114f45760006114f7565b60025b6001600160a01b038816600080516020614e808339815191521461151c57600061151f565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b03161461155857600061155b565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611586576000611589565b60035b6001600160a01b038716600080516020614eec833981519152146115ae5760006115b1565b60025b6001600160a01b038816600080516020614e80833981519152146115d65760006115d9565b60015b01010160ff16905081600f0b600014806115f6575080600f0b6000145b15611602575050611294565b6116306001600160a01b0387167345f783cce6b7ff23b2ab2d70e416cdb7d6055f518663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b60006001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c53146116d35760006116d6565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611701576000611704565b60035b6001600160a01b038716600080516020614eec8339815191521461172957600061172c565b60025b6001600160a01b038816600080516020614e8083398151915214611751576000611754565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b031614611792576000611795565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec7146117c05760006117c3565b60035b6001600160a01b038716600080516020614eec833981519152146117e85760006117eb565b60025b6001600160a01b038816600080516020614e8083398151915214611810576000611813565b60015b01010160ff16905081600f0b60001480611830575080600f0b6000145b1561183c575050611294565b61186a6001600160a01b0387167379a8c46dea5ada233abaffd40f3a0a2b1e5a4f278663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b60006001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f511461190d576000611910565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec71461193b57600061193e565b60035b6001600160a01b038716600080516020614eec83398151915214611963576000611966565b60025b6001600160a01b038816600080516020614e808339815191521461198b57600061198e565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b0316146119cc5760006119cf565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec7146119fa5760006119fd565b60035b6001600160a01b038716600080516020614eec83398151915214611a22576000611a25565b60025b6001600160a01b038816600080516020614e8083398151915214611a4a576000611a4d565b60015b01010160ff16905081600f0b60001480611a6a575080600f0b6000145b15611a76575050611294565b611aa46001600160a01b03871673a5407eae9ba41422680e2e00537571bcc53efbfd8663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b611b2c846001600160a01b03166131ac565b611c7357604080516332a5d5bf60e01b81526001600160a01b0386166004820152905160009173f451dbd7ba14bfa7b1b78a766d3ed438f79ee1d1916332a5d5bf91602480820192602092909190829003018186803b158015611b8e57600080fd5b505afa158015611ba2573d6000803e3d6000fd5b505050506040513d6020811015611bb857600080fd5b50519050611bd66001600160a01b038616828563ffffffff6134ba16565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015611c1c57600080fd5b505af1158015611c30573d6000803e3d6000fd5b505050506040513d6020811015611c4657600080fd5b50611c6d90508185611c676001600160a01b0383163063ffffffff6132f716565b8561081c565b50611294565b611c85836001600160a01b03166131ac565b61129457604080516332a5d5bf60e01b81526001600160a01b0385166004820152905160009173f451dbd7ba14bfa7b1b78a766d3ed438f79ee1d1916332a5d5bf91602480820192602092909190829003018186803b158015611ce757600080fd5b505afa158015611cfb573d6000803e3d6000fd5b505050506040513d6020811015611d1157600080fd5b50519050611d218582858561081c565b6001600160a01b03811663db006a75611d40823063ffffffff6132f716565b6040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015611d7657600080fd5b505af1158015611d8a573d6000803e3d6000fd5b505050506040513d6020811015611da057600080fd5b50611dbc90506001600160a01b0385163063ffffffff6132f716565b5050611294565b6001600160a01b038416600080516020614e808339815191521415611eb857611e106001600160a01b0385167306af07097c9eeb7fd685c692751d5c66db49c2158463ffffffff6134ba16565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b158015611e6957600080fd5b505af1158015611e7d573d6000803e3d6000fd5b50611eb392507306af07097c9eeb7fd685c692751d5c66db49c2159150859050611ead823063ffffffff6132f716565b8461081c565b611294565b6001600160a01b038316600080516020614e80833981519152141561129457611ef7847306af07097c9eeb7fd685c692751d5c66db49c215848461081c565b604080516370a0823160e01b8152306004820181905291517306af07097c9eeb7fd685c692751d5c66db49c2159263ef693bed92909184916370a08231916024808301926020929190829003018186803b158015611f5457600080fd5b505afa158015611f68573d6000803e3d6000fd5b505050506040513d6020811015611f7e57600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b039093166004840152602483019190915251604480830192600092919082900301818387803b158015611fce57600080fd5b505af1158015611fe2573d6000803e3d6000fd5b50505050611294565b611ffd846001600160a01b03166131ac565b6121c35760408051635f5418f360e01b81526001600160a01b0386166004820152905160009173ed8b133b7b88366e01bb9e38305ab11c2652149491635f5418f391602480820192602092909190829003018186803b15801561205f57600080fd5b505afa158015612073573d6000803e3d6000fd5b505050506040513d602081101561208957600080fd5b50516040805163797a759360e11b815290519192506121209173398ec7346dcd622edc5ae82352f02be94c62d1199163f2f4eb26916004808301926020929190829003018186803b1580156120dd57600080fd5b505afa1580156120f1573d6000803e3d6000fd5b505050506040513d602081101561210757600080fd5b50516001600160a01b038716908563ffffffff6134ba16565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b15801561218a57600080fd5b505af115801561219e573d6000803e3d6000fd5b50505050611c6d8185611c6730856001600160a01b03166132f790919063ffffffff16565b6121d5836001600160a01b03166131ac565b6112945760408051635f5418f360e01b81526001600160a01b0385166004820152905160009173ed8b133b7b88366e01bb9e38305ab11c2652149491635f5418f391602480820192602092909190829003018186803b15801561223757600080fd5b505afa15801561224b573d6000803e3d6000fd5b505050506040513d602081101561226157600080fd5b505190506122718582858561081c565b604080516370a0823160e01b815230600482015290516001600160a01b0383169163db006a759183916370a08231916024808301926020929190829003018186803b1580156122bf57600080fd5b505afa1580156122d3573d6000803e3d6000fd5b505050506040513d60208110156122e957600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561232957600080fd5b505af115801561233d573d6000803e3d6000fd5b5050505050611294565b600073c12a7e093832e2d2267df225baca60bd2b74c65f63901754d76123756001600160a01b0388166131ac565b61237f5786612382565b60005b612394876001600160a01b03166131ac565b61239e57866123a1565b60005b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b031681526020019250505060206040518083038186803b15801561240057600080fd5b505afa158015612414573d6000803e3d6000fd5b505050506040513d602081101561242a57600080fd5b505190506124486001600160a01b038616828563ffffffff6134ba16565b806001600160a01b031663d5bcb9b5612469876001600160a01b03166131ac565b612474576000612476565b845b612488886001600160a01b03166131ac565b6124925787612495565b60005b6124a7886001600160a01b03166131ac565b6124b157876124b4565b60005b604080516001600160e01b031960e087901b1681526001600160a01b0393841660048201529190921660248201526044810188905260006064820152734d37f28d2db99e8d35a6c725a5f1749a085850a36084820152905160a480830192602092919082900301818588803b15801561252c57600080fd5b505af1158015612540573d6000803e3d6000fd5b50505050506040513d602081101561255757600080fd5b50505050505050565b610aa1848484846135b3565b61129484600080516020614e60833981519152858585613965565b61129484600080516020614e80833981519152858585613965565b61129484600080516020614eec833981519152858585613965565b60006001600160a01b038516738e870d67f660d95d5be530380d0ec0bd388289e1146125ea5760006125ed565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec71461261857600061261b565b60035b6001600160a01b038716600080516020614eec83398151915214612640576000612643565b60025b6001600160a01b038816600080516020614e808339815191521461266857600061266b565b60015b01010160ff1690506000738e870d67f660d95d5be530380d0ec0bd388289e16001600160a01b0316856001600160a01b0316146126a95760006126ac565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec7146126d75760006126da565b60035b6001600160a01b038716600080516020614eec833981519152146126ff576000612702565b60025b6001600160a01b038816600080516020614e808339815191521461272757600061272a565b60015b01010160ff16905081600f0b60001480612747575080600f0b6000145b15612753575050611294565b6127816001600160a01b0387167306364f10b501e868329afbc005b3492902d6c7638663ffffffff6134ba16565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517306364f10b501e868329afbc005b3492902d6c7639263a6417ed6926084808201939182900301818387803b15801561127957600080fd5b60006001600160a01b038516732260fac5e5542a773aa44fbcfedf7c193bc2c59914612824576000612827565b60025b6001600160a01b03861673eb4c2781e4eba804ce9a9803c67d0893436bb27d14612852576000612855565b60015b0160ff1690506000732260fac5e5542a773aa44fbcfedf7c193bc2c5996001600160a01b0386161461288857600061288b565b60025b6001600160a01b03861673eb4c2781e4eba804ce9a9803c67d0893436bb27d146128b65760006128b9565b60015b0160ff16905081600f0b600014806128d4575080600f0b6000145b156128e0575050611294565b61290e6001600160a01b0387167393054188d876f558f4a66b2ef1d97d16edf0895b8663ffffffff6134ba16565b60408051630f7c084960e21b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517393054188d876f558f4a66b2ef1d97d16edf0895b92633df02124926084808201939182900301818387803b15801561127957600080fd5b60006001600160a01b038516730316eb71485b0ab14103307bf65a021042c6d380146129b15760006129b4565b60035b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c599146129df5760006129e2565b60025b6001600160a01b038716731bbe271d15bb64df0bc6cd28df9ff322f2ebd84714612a0d576000612a10565b60015b010160ff1690506000730316eb71485b0ab14103307bf65a021042c6d3806001600160a01b0316856001600160a01b031614612a4d576000612a50565b60035b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c59914612a7b576000612a7e565b60025b6001600160a01b038716731bbe271d15bb64df0bc6cd28df9ff322f2ebd84714612aa9576000612aac565b60015b010160ff16905081600f0b60001480612ac8575080600f0b6000145b15612ad4575050611294565b612b026001600160a01b038716739726e9314ef1b96e45f40056bed61a088897313e8663ffffffff6134ba16565b60408051630f7c084960e21b8152600019808501600f90810b810b6004840152908401810b900b6024820152604481018690526000606482018190529151739726e9314ef1b96e45f40056bed61a088897313e92633df02124926084808201939182900301818387803b15801561127957600080fd5b612ba66001600160a01b0385167303ef3f37856bd08eb47e2de7abc4ddd2c19b60f28463ffffffff6134ba16565b60408051630df791e560e41b81526001600160a01b038681166004830152851660248201526044810184905290517303ef3f37856bd08eb47e2de7abc4ddd2c19b60f29163df791e5091606480830192600092919082900301818387803b158015612c1057600080fd5b505af1158015610e31573d6000803e3d6000fd5b612c526001600160a01b03851673a8253a440be331dc4a7395b73948cca6f19dc97d8463ffffffff6134ba16565b604080516303ff4c0160e31b81526001600160a01b0386811660048301528516602482015260448101849052600060648201819052603242016084830152915173a8253a440be331dc4a7395b73948cca6f19dc97d92631ffa60089260a480820193602093909283900390910190829087803b158015612cd157600080fd5b505af1158015612ce5573d6000803e3d6000fd5b505050506040513d6020811015610a9f57600080fd5b612d296001600160a01b03851673e2f2a5c287993345a840db3b0845fbc70f5935a58463ffffffff6134ba16565b60408051631ba0488760e21b81526001600160a01b0386811660048301528516602482015260448101849052306064820152905173e2f2a5c287993345a840db3b0845fbc70f5935a591636e81221c9160848083019260209291908290030181600087803b158015612cd157600080fd5b60006001600160a01b03851673fe18be6b3bd88a2d2a7f928d00292e7a9963cfc614612dc7576000612dca565b60035b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c59914612df5576000612df8565b60025b6001600160a01b03871673eb4c2781e4eba804ce9a9803c67d0893436bb27d14612e23576000612e26565b60015b010160ff169050600073fe18be6b3bd88a2d2a7f928d00292e7a9963cfc66001600160a01b0316856001600160a01b031614612e63576000612e66565b60035b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c59914612e91576000612e94565b60025b6001600160a01b03871673eb4c2781e4eba804ce9a9803c67d0893436bb27d14612ebf576000612ec2565b60015b010160ff16905081600f0b60001480612ede575080600f0b6000145b15612eea575050611294565b612f186001600160a01b038716737fc77b5c7614e1533320ea6ddc2eb61fa00a97148663ffffffff6134ba16565b60408051630f7c084960e21b8152600019808501600f90810b810b6004840152908401810b900b6024820152604481018690526000606482018190529151737fc77b5c7614e1533320ea6ddc2eb61fa00a971492633df02124926084808201939182900301818387803b15801561127957600080fd5b61129484848484600061397c565b61129484848484600161397c565b61129484848484600261397c565b611294848484846001685a434ecd46efdcc7c760b11b0319613ded565b61129484848484600161543360e81b0319613ded565b6112948484848460016b2c2466af65a2f7eba2a7a463609a1b0319613ded565b6112948484848461301c8989614611565b613ded565b61302e8460008484612347565b6112946000844784612347565b61305584600080516020614e808339815191528484612347565b604080516370a0823160e01b8152306004820152905161129491600080516020614e8083398151915291869183916370a0823191602480820192602092909190829003018186803b1580156130a957600080fd5b505afa1580156130bd573d6000803e3d6000fd5b505050506040513d60208110156130d357600080fd5b505184612347565b6130f584600080516020614eec8339815191528484612347565b604080516370a0823160e01b8152306004820152905161129491600080516020614eec83398151915291869183916370a0823191602480820192602092909190829003018186803b1580156130a957600080fd5b6000828201838110156131a3576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60006001600160a01b03821615806131a657506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1492915050565b806131ef57611294565b6131f8846131ac565b156132dc576001600160a01b038316331480156132155750803410155b6132505760405162461bcd60e51b815260040180806020018281038252602b815260200180614ea0602b913960400191505060405180910390fd5b6001600160a01b0382163014613298576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015613296573d6000803e3d6000fd5b505b80341115611eb357336108fc6132b4348463ffffffff61488c16565b6040518115909202916000818181858888f19350505050158015611c6d573d6000803e3d6000fd5b6112946001600160a01b03851684848463ffffffff6148ce16565b6000613302836131ac565b1561331857506001600160a01b038116316131a6565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561336e57600080fd5b505afa158015613382573d6000803e3d6000fd5b505050506040513d602081101561339857600080fd5b505190506131a6565b6000826133b0575060006131a6565b828202828482816133bd57fe5b04146131a35760405162461bcd60e51b8152600401808060200182810382526021815260200180614ecb6021913960400191505060405180910390fd5b60006131a383836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614928565b60008161344b575060016134b3565b613454846131ac565b15613495576040516001600160a01b0384169083156108fc029084906000818181858888f1935050505015801561348f573d6000803e3d6000fd5b506134b3565b6134af6001600160a01b038516848463ffffffff6149ca16565b5060015b9392505050565b6134c3836131ac565b6135ae57806134ec576134e76001600160a01b03841683600063ffffffff614a1c16565b6135ae565b60408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561353d57600080fd5b505afa158015613551573d6000803e3d6000fd5b505050506040513d602081101561356757600080fd5b5051905081811015611294578015613594576135946001600160a01b03851684600063ffffffff614a1c16565b6112946001600160a01b038516848463ffffffff614a1c16565b505050565b60006135c7856001600160a01b03166131ac565b1561362f57600080516020614e608339815191526001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561361557600080fd5b505af1158015613629573d6000803e3d6000fd5b50505050505b6000613643866001600160a01b03166131ac565b61364d578561365d565b600080516020614e608339815191525b90506000613673866001600160a01b03166131ac565b61367d578561368d565b600080516020614e608339815191525b6040805163e6a4390560e01b81526001600160a01b038581166004830152831660248201529051919250600091735c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f9163e6a43905916044808301926020929190829003018186803b1580156136f557600080fd5b505afa158015613709573d6000803e3d6000fd5b505050506040513d602081101561371f57600080fd5b5051905061373e6001600160a01b03821684848963ffffffff614b2f16565b935061375a6001600160a01b038416828863ffffffff61343c16565b50816001600160a01b0316836001600160a01b031610156137f3576040805163022c0d9f60e01b815260006004820181905260248201879052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b1580156137d657600080fd5b505af11580156137ea573d6000803e3d6000fd5b5050505061386d565b6040805163022c0d9f60e01b815260048101869052600060248201819052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b15801561385457600080fd5b505af1158015613868573d6000803e3d6000fd5b505050505b61387f876001600160a01b03166131ac565b1561395a57604080516370a0823160e01b81523060048201529051600080516020614e6083398151915291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156138d757600080fd5b505afa1580156138eb573d6000803e3d6000fd5b505050506040513d602081101561390157600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561394157600080fd5b505af1158015613955573d6000803e3d6000fd5b505050505b505050949350505050565b610a9f8484613976888887876135b3565b846135b3565b60607365e67cbc342712df67494acefc06fe951ee9398263bfdbfc436139aa6001600160a01b0389166131ac565b6139b457876139c4565b600080516020614e608339815191525b6139d6886001600160a01b03166131ac565b6139e057876139f0565b600080516020614e608339815191525b856001016040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160a01b03166001600160a01b03168152602001828152602001935050505060006040518083038186803b158015613a5a57600080fd5b505afa158015613a6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015613a9757600080fd5b8101908080516040519392919084600160201b821115613ab657600080fd5b908301906020820185811115613acb57600080fd5b82518660208202830111600160201b82111715613ae757600080fd5b82525081516020918201928201910280838360005b83811015613b14578181015183820152602001613afc565b505050509050016040525050509050613b35866001600160a01b03166131ac565b15613b9d57600080516020614e608339815191526001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b158015613b8357600080fd5b505af1158015613b97573d6000803e3d6000fd5b50505050505b613bf7818381518110613bac57fe5b602002602001015185613bc7896001600160a01b03166131ac565b613bd15788613be1565b600080516020614e608339815191525b6001600160a01b0316919063ffffffff6134ba16565b808281518110613c0357fe5b60200260200101516001600160a01b0316638201aa3f613c2b886001600160a01b03166131ac565b613c355787613c45565b600080516020614e608339815191525b86613c58896001600160a01b03166131ac565b613c625788613c72565b600080516020614e608339815191525b60006000196040518663ffffffff1660e01b815260040180866001600160a01b03166001600160a01b03168152602001858152602001846001600160a01b03166001600160a01b03168152602001838152602001828152602001955050505050506040805180830381600087803b158015613cec57600080fd5b505af1158015613d00573d6000803e3d6000fd5b505050506040513d6040811015613d1657600080fd5b50613d2b90506001600160a01b0386166131ac565b15610a9f57604080516370a0823160e01b81523060048201529051600080516020614e6083398151915291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015613d8357600080fd5b505afa158015613d97573d6000803e3d6000fd5b505050506040513d6020811015613dad57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561127957600080fd5b604080516001808252818301909252849160609190602080830190803883390190505090508281600081518110613e2057fe5b602002602001018181525050613e3e876001600160a01b03166131ac565b61423957606073a1c0fa73c39cfbcc11ec9eb1afc665aba9996e2c6001600160a01b03166361e597f9896001856000604051908082528060200260200182016040528015613e96578160200160208202803883390190505b506040518563ffffffff1660e01b815260040180856001600160a01b03166001600160a01b03168152602001846003811115613ece57fe5b60ff1681526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015613f16578181015183820152602001613efe565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015613f55578181015183820152602001613f3d565b50505050905001965050505050505060006040518083038186803b158015613f7c57600080fd5b505afa158015613f90573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015613fb957600080fd5b8101908080516040519392919084600160201b821115613fd857600080fd5b908301906020820185811115613fed57600080fd5b8251600160201b81118282018810171561400657600080fd5b82525081516020918201929091019080838360005b8381101561403357818101518382015260200161401b565b50505050905090810190601f1680156140605780820380516001836020036101000a031916815260200191505b50604052509192506140999150506001600160a01b038916739aab3f75489902f3a48495025729a0af77d4b11e8863ffffffff6134ba16565b739aab3f75489902f3a48495025729a0af77d4b11e6001600160a01b031663ae591d54898573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee306000196000734d37f28d2db99e8d35a6c725a5f1749a085850a3600a8a6040518a63ffffffff1660e01b8152600401808a6001600160a01b03166001600160a01b03168152602001898152602001886001600160a01b03166001600160a01b03168152602001876001600160a01b03166001600160a01b03168152602001868152602001858152602001846001600160a01b03166001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b838110156141b557818101518382015260200161419d565b50505050905090810190601f1680156141e25780820380516001836020036101000a031916815260200191505b509a5050505050505050505050602060405180830381600087803b15801561420957600080fd5b505af115801561421d573d6000803e3d6000fd5b505050506040513d602081101561423357600080fd5b50519250505b61424b866001600160a01b03166131ac565b61255757606073a1c0fa73c39cfbcc11ec9eb1afc665aba9996e2c6001600160a01b03166381efcbdd8860018560006040519080825280602002602001820160405280156142a3578160200160208202803883390190505b506040518563ffffffff1660e01b815260040180856001600160a01b03166001600160a01b031681526020018460038111156142db57fe5b60ff1681526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561432357818101518382015260200161430b565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561436257818101518382015260200161434a565b50505050905001965050505050505060006040518083038186803b15801561438957600080fd5b505afa15801561439d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156143c657600080fd5b8101908080516040519392919084600160201b8211156143e557600080fd5b9083019060208201858111156143fa57600080fd5b8251600160201b81118282018810171561441357600080fd5b82525081516020918201929091019080838360005b83811015614440578181015183820152602001614428565b50505050905090810190601f16801561446d5780820380516001836020036101000a031916815260200191505b506040525050509050739aab3f75489902f3a48495025729a0af77d4b11e6001600160a01b031663ae591d548473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee868b306000196000734d37f28d2db99e8d35a6c725a5f1749a085850a3600a8b6040518b63ffffffff1660e01b8152600401808a6001600160a01b03166001600160a01b03168152602001898152602001886001600160a01b03166001600160a01b03168152602001876001600160a01b03166001600160a01b03168152602001868152602001858152602001846001600160a01b03166001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561459357818101518382015260200161457b565b50505050905090810190601f1680156145c05780820380516001836020036101000a031916815260200191505b509a50505050505050505050506020604051808303818588803b1580156145e657600080fd5b505af11580156145fa573d6000803e3d6000fd5b50505050506040513d602081101561128d57600080fd5b6000614625836001600160a01b03166131ac565b158015614641575061463f826001600160a01b03166131ac565b155b1561464e575060006131a6565b606073c8fb12402cb16970f3c5f4b48ff68eb9d1289301633d3dc52c61467c6001600160a01b0387166131ac565b6146865785614688565b845b6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060006040518083038186803b1580156146ce57600080fd5b505afa1580156146e2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561470b57600080fd5b8101908080516040519392919084600160201b82111561472a57600080fd5b90830190602082018581111561473f57600080fd5b82518660208202830111600160201b8211171561475b57600080fd5b82525081516020918201928201910280838360005b83811015614788578181015183820152602001614770565b50505050905001604052505050905060008090505b81518110156148815760f88282815181106147b457fe5b602002602001015160001c901c60bb141580156147f757508181815181106147d857fe5b60200260200101516001685a434ecd46efdcc7c760b11b031960001b14155b8015614822575081818151811061480a57fe5b6020026020010151600161543360e81b031960001b14155b8015614857575081818151811061483557fe5b602002602001015160016b2c2466af65a2f7eba2a7a463609a1b031960001b14155b156148795781818151811061486857fe5b6020026020010151925050506131a6565b60010161479d565b506000949350505050565b60006131a383836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250614be6565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611294908590614c40565b600081836149b45760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614979578181015183820152602001614961565b50505050905090810190601f1680156149a65780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816149c057fe5b0495945050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526135ae908490614c40565b801580614aa2575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015614a7457600080fd5b505afa158015614a88573d6000803e3d6000fd5b505050506040513d6020811015614a9e57600080fd5b5051155b614add5760405162461bcd60e51b8152600401808060200182810382526036815260200180614f5c6036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526135ae908490614c40565b600080614b4b6001600160a01b0386168763ffffffff6132f716565b90506000614b686001600160a01b0386168863ffffffff6132f716565b90506000614b7e856103e563ffffffff6133a116565b90506000614b92828463ffffffff6133a116565b90506000614bb883614bac876103e863ffffffff6133a116565b9063ffffffff61314916565b90508015614bd557614bd0828263ffffffff6133fa16565b614bd8565b60005b9a9950505050505050505050565b60008184841115614c385760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315614979578181015183820152602001614961565b505050900390565b614c52826001600160a01b0316614df8565b614ca3576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310614ce15780518252601f199092019160209182019101614cc2565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614d43576040519150601f19603f3d011682016040523d82523d6000602084013e614d48565b606091505b509150915081614d9f576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b80511561129457808060200190516020811015614dbb57600080fd5b50516112945760405162461bcd60e51b815260040180806020018281038252602a815260200180614f32602a913960400191505060405180910390fd5b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590614e2c57508115155b949350505050565b6040518061044001604052806022905b614e5d815260200190600190039081614e445790505090565bfefe000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f57726f6e6720757365616765206f66204554482e756e6976657273616c5472616e7366657246726f6d2829536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb484f6e6553706c69743a2052657475726e20616d6f756e7420776173206e6f7420656e6f7567685361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820de530c3963242374be6d69dd06ead3f3520b8b1e26334841b7db5b7f12b8767964736f6c63430005110032
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b97274ad8102cd447578338e66a529611c9609de
-----Decoded View---------------
Arg [0] : _oneSplitView (address): 0xb97274AD8102cD447578338E66a529611C9609DE
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000b97274ad8102cd447578338e66a529611c9609de
Deployed Bytecode Sourcemap
128442:25370:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;128745:10;128759:9;128745:23;;128737:32;;;;;;128442:25370;128785:515;;8:9:-1;5:2;;;30:1;27;20:12;5:2;128785:515:0;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;;;;;;128785:515:0;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;128785:515:0;;;;;;;;;;;;;;;;;;129308:627;;8:9:-1;5:2;;;30:1;27;20:12;5:2;129308:627:0;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;;;;;;129308:627:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;129308:627:0;;;;;;;;;;;;;;;;;;;129943:3064;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;;;;;129943:3064:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;129943:3064:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;129943:3064:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;129943:3064:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;129943:3064:0;;-1:-1:-1;;129943:3064:0;;;-1:-1:-1;129943:3064:0;;-1:-1:-1;;129943:3064:0:i;:::-;;;;;;;;;;;;;;;;128494:33;;8:9:-1;5:2;;;30:1;27;20:12;5:2;128494:33:0;;;:::i;:::-;;;;-1:-1:-1;;;;;128494:33:0;;;;;;;;;;;;;;128785:515;129007:20;129042:29;129132:160;129171:9;129195;129219:6;129240:5;129260;129280:1;129132:24;:160::i;:::-;129099:193;;;;-1:-1:-1;128785:515:0;;-1:-1:-1;;;;;;;128785:515:0:o;129308:627::-;129586:20;129725:12;;:202;;;-1:-1:-1;;;129725:202:0;;-1:-1:-1;;;;;129725:202:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;129586:20;;129661:29;;129725:12;;:37;;:202;;;;;129586:20;;129725:202;;;;;;;:12;:202;;;5:2:-1;;;;30:1;27;20:12;5:2;129725:202:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;129725:202:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;129725:202:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;129725:202:0;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;11:20;;8:2;;;44:1;41;34:12;8:2;62:21;;;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;219:3;213:10;331:9;325:2;311:12;307:21;289:16;285:44;282:59;-1:-1;;;247:12;244:29;233:116;230:2;;;362:1;359;352:12;230:2;373:25;;-1:-1;129725:202:0;;421:4:-1;412:14;;;;129725:202:0;;;;;412:14:-1;129725:202:0;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;129725:202:0;;;;;;;;;;;129718:209;;;;;;129308:627;;;;;;;;;;:::o;129943:3064::-;130193:20;130243:9;-1:-1:-1;;;;;130230:22:0;:9;-1:-1:-1;;;;;130230:22:0;;130226:68;;;-1:-1:-1;130276:6:0;130269:13;;130226:68;130306;;:::i;:::-;:1152;;;;;;;;130392:14;130306:1152;;;;130421:14;130306:1152;;;;130450:13;130306:1152;;;;130478:12;130306:1152;;;;130505:20;130306:1152;;;;130540:16;130306:1152;;;;130571:13;130306:1152;;;;130599:19;130306:1152;;;;130633:21;130306:1152;;;;130669:22;130306:1152;;;;130706:18;130306:1152;;;;130739:18;130306:1152;;;;130772:16;130306:1152;;;;130803:16;130306:1152;;;;130834:19;130306:1152;;;;130868:19;130306:1152;;;;130902:20;130306:1152;;;;130937:15;130306:1152;;;;130967:18;130306:1152;;;;131000:16;130306:1152;;;;131031:17;130306:1152;;;;131063:12;130306:1152;;;;131090:18;130306:1152;;;;131123:16;130306:1152;;;;131154:16;130306:1152;;;;131185:16;130306:1152;;;;131216:16;130306:1152;;;;131247:13;130306:1152;;;;131275:13;130306:1152;;;;131303:13;130306:1152;;;;131331:13;130306:1152;;;;131359:19;130306:1152;;;;131393:19;130306:1152;;;;131427:20;130306:1152;;;;;131502:15;131479:12;:19;:38;;131471:117;;;;-1:-1:-1;;;131471:117:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;131601:13;;;131668:206;131689:12;:19;131685:1;:23;131668:206;;;131752:1;131734:12;131747:1;131734:15;;;;;;;;;;;;;;:19;131730:133;;;131782:26;131792:12;131805:1;131792:15;;;;;;;;;;;;;;131782:5;:9;;:26;;;;:::i;:::-;131774:34;;131846:1;131827:20;;131730:133;131710:3;;131668:206;;;-1:-1:-1;131890:10:0;131886:193;;131921:17;:9;-1:-1:-1;;;;;131921:15:0;;:17::i;:::-;131917:123;;;131959:30;;:10;;131979:9;131959:30;;;;;;;;;131979:9;131959:10;:30;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;131959:30:0;132015:9;132008:16;;;;;;;131917:123;132061:6;132054:13;;;;;;;131886:193;132091:66;-1:-1:-1;;;;;132091:31:0;;132123:10;132143:4;132150:6;132091:66;:31;:66;:::i;:::-;132168:23;132194:43;-1:-1:-1;;;;;132194:28:0;;132231:4;132194:43;:28;:43;:::i;:::-;132168:69;-1:-1:-1;132255:6:0;132250:433;132271:12;:19;132267:1;:23;132250:433;;;132316:12;132329:1;132316:15;;;;;;;;;;;;;;132335:1;132316:20;132312:69;;;132357:8;;132312:69;132397:18;132418:38;132450:5;132418:27;132429:12;132442:1;132429:15;;;;;;;;;;;;;;132418:6;:10;;:27;;;;:::i;:::-;:31;:38;:31;:38;:::i;:::-;132397:59;;132480:16;132475:1;:21;132471:90;;;-1:-1:-1;132530:15:0;132471:90;132594:10;132575:29;;;;132619:52;132631:9;132642;132653:10;132665:5;132619:8;132628:1;132619:11;;;;;;;;;;;:52;;:::i;:::-;132250:433;;132292:3;;132250:433;;;-1:-1:-1;132710:43:0;-1:-1:-1;;;;;132710:28:0;;132747:4;132710:43;:28;:43;:::i;:::-;132695:58;;132788:9;132772:12;:25;;132764:76;;;;-1:-1:-1;;;132764:76:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;132851:53;-1:-1:-1;;;;;132851:27:0;;132879:10;132891:12;132851:53;:27;:53;:::i;:::-;-1:-1:-1;132915:84:0;132943:10;132955:43;-1:-1:-1;;;;;132955:28:0;;132992:4;132955:43;:28;:43;:::i;:::-;-1:-1:-1;;;;;132915:27:0;;;:84;;:27;:84;:::i;:::-;;129943:3064;;;;;;;;;;;;;:::o;128494:33::-;;;-1:-1:-1;;;;;128494:33:0;;:::o;139894:859::-;140075:6;140099:17;-1:-1:-1;;;;;140099:15:0;;;:17::i;:::-;140094:361;;140165:37;;;-1:-1:-1;;;140165:37:0;;-1:-1:-1;;;;;140165:37:0;;;;;;;;140133:29;;71991:42;;140165:26;;:37;;;;;;;;;;;;;;;71991:42;140165:37;;;5:2:-1;;;;30:1;27;20:12;5:2;140165:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;140165:37:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;140165:37:0;;-1:-1:-1;;;;;;140221:35:0;;;140217:227;;140277:63;-1:-1:-1;;;;;140277:26:0;;140312:12;140327;140277:63;:26;:63;:::i;:::-;140374:54;;;-1:-1:-1;;;140374:54:0;;;;;;;;140421:1;140374:54;;;;140424:3;140374:54;;;;;;-1:-1:-1;;;;;140374:32:0;;;;;:54;;;;;;;;;;;;;;-1:-1:-1;140374:32:0;:54;;;5:2:-1;;;;30:1;27;20:12;5:2;140374:54:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;140374:54:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;140374:54:0;;-1:-1:-1;140217:227:0;140094:361;;140472:17;:9;-1:-1:-1;;;;;140472:15:0;;:17::i;:::-;140467:279;;140536:37;;;-1:-1:-1;;;140536:37:0;;-1:-1:-1;;;;;140536:37:0;;;;;;;;140506:27;;71991:42;;140536:26;;:37;;;;;;;;;;;;;;;71991:42;140536:37;;;5:2:-1;;;;30:1;27;20:12;5:2;140536:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;140536:37:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;140536:37:0;;-1:-1:-1;;;;;;140592:33:0;;;140588:147;;140661:10;-1:-1:-1;;;;;140661:30:0;;140698:12;140712:1;140715:3;140661:58;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;140661:58:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;140661:58:0;;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;140661:58:0;;-1:-1:-1;140588:147:0;140467:279;;139894:859;;;;;:::o;144775:214::-;144945:36;;;-1:-1:-1;;;144945:36:0;;;;;;;;;;;;;;;;;;;;;;;;;;;148139:625;148296:28;72132:42;-1:-1:-1;;;;;148342:32:0;;:49;;;;;;;;;;;;;-1:-1:-1;;;148342:49:0;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;148342:49:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;148342:49:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;148342:49:0;;-1:-1:-1;148403:21:0;72276:42;148427:36;148478:17;-1:-1:-1;;;;;148478:15:0;;;:17::i;:::-;:48;;148517:9;148478:48;;;70270:42;148478:48;148541:17;:9;-1:-1:-1;;;;;148541:15:0;;:17::i;:::-;:48;;148580:9;148541:48;;;70270:42;148541:48;148427:173;;;;;;;;;;;;;-1:-1:-1;;;;;148427:173:0;-1:-1:-1;;;;;148427:173:0;;;;;;-1:-1:-1;;;;;148427:173:0;-1:-1:-1;;;;;148427:173:0;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;148427:173:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;148427:173:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;148427:173:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;148427:173:0;;;;;;;;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;219:3;213:10;331:9;325:2;311:12;307:21;289:16;285:44;282:59;-1:-1;;;247:12;244:29;233:116;230:2;;;362:1;359;352:12;230:2;373:25;;-1:-1;148427:173:0;;421:4:-1;412:14;;;;148427:173:0;;;;;412:14:-1;148427:173:0;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;148427:173:0;;;;;;;;;;;148403:197;;148611:58;148646:13;148662:6;148611:9;-1:-1:-1;;;;;148611:26:0;;;:58;;;;;:::i;:::-;148680:13;-1:-1:-1;;;;;148680:21:0;;148708:17;:9;-1:-1:-1;;;;;148708:15:0;;:17::i;:::-;:30;;148737:1;148708:30;;;148728:6;148708:30;148740:4;148746:6;148754:1;148680:76;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;148680:76:0;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;148680:76:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;148680:76:0;;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;;;148139:625:0:o;148772:678::-;148932:17;:9;-1:-1:-1;;;;;148932:15:0;;:17::i;:::-;148928:78;;;-1:-1:-1;;;;;;;;;;;;;;;;148966:12:0;;148985:6;148966:28;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;148966:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;148966:28:0;;;;;148928:78;149018:19;149040:17;:9;-1:-1:-1;;;;;149040:15:0;;:17::i;:::-;:36;;149067:9;149040:36;;;-1:-1:-1;;;;;;;;;;;149040:36:0;149018:58;-1:-1:-1;149087:61:0;-1:-1:-1;;;;;149087:29:0;;72647:42;149141:6;149087:61;:29;:61;:::i;:::-;72647:42;149159:27;149201:17;-1:-1:-1;;;;;149201:15:0;;;:17::i;:::-;:36;;149228:9;149201:36;;;-1:-1:-1;;;;;;;;;;;149201:36:0;149252:6;149273:17;:9;-1:-1:-1;;;;;149273:15:0;;:17::i;:::-;:36;;149300:9;149273:36;;;-1:-1:-1;;;;;;;;;;;149273:36:0;149159:177;;;-1:-1:-1;;;;;;149159:177:0;;;;;;;-1:-1:-1;;;;;149159:177:0;;;;;;;;;;;;;;;;;;;;149324:1;149159:177;;;;;;;;;;;;;;;;;;;;-1:-1:-1;149159:177:0;;;;5:2:-1;;;;30:1;27;20:12;5:2;149159:177:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;149159:177:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;149353:17:0;;-1:-1:-1;;;;;;149353:15:0;;;:17::i;:::-;149349:94;;;149401:29;;;-1:-1:-1;;;149401:29:0;;149424:4;149401:29;;;;;;-1:-1:-1;;;;;;;;;;;70357:42:0;149387:13;;70357:42;;149401:14;;:29;;;;;;;;;;;;;;70357:42;149401:29;;;5:2:-1;;;;30:1;27;20:12;5:2;149401:29:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;149401:29:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;149401:29:0;149387:44;;;-1:-1:-1;;;;;;149387:44:0;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;149387:44:0;;;;;;;-1:-1:-1;149387:44:0;;;;5:2:-1;;;;30:1;27;20:12;5:2;149387:44:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;149387:44:0;;;;148772:678;;;;;:::o;133038:522::-;133202:8;-1:-1:-1;;;;;133243:17:0;;-1:-1:-1;;;;;;;;;;;133243:17:0;:25;;133267:1;133243:25;;;133263:1;133243:25;-1:-1:-1;;;;;133214:16:0;;-1:-1:-1;;;;;;;;;;;133214:16:0;:24;;133237:1;133214:24;;;133233:1;133214:24;133213:56;133202:67;;;-1:-1:-1;133280:8:0;-1:-1:-1;;;;;;;;;;;;;;;;133321:17:0;;;:25;;133345:1;133321:25;;;133341:1;133321:25;-1:-1:-1;;;;;133292:16:0;;-1:-1:-1;;;;;;;;;;;133292:16:0;:24;;133315:1;133292:24;;;133311:1;133292:24;133291:56;133280:67;;;;133362:1;:6;;133367:1;133362:6;:16;;;;133372:1;:6;;133377:1;133372:6;133362:16;133358:55;;;133395:7;;;;133358:55;133425:58;-1:-1:-1;;;;;133425:26:0;;72745:42;133476:6;133425:58;:26;:58;:::i;:::-;133494;;;-1:-1:-1;;;133494:58:0;;-1:-1:-1;;133528:5:0;;;133494:58;;;;;;;;;;133535:5;;;133494:58;;;;;;;;;;;;;;133550:1;133494:58;;;;;;;;72745:42;;133494:33;;:58;;;;;;;;;;;133550:1;72745:42;133494:58;;;5:2:-1;;;;30:1;27;20:12;5:2;133494:58:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;133494:58:0;;;;133038:522;;;;;;;:::o;133568:622::-;133728:8;-1:-1:-1;;;;;133825:17:0;;70710:42;133825:17;:25;;133849:1;133825:25;;;133845:1;133825:25;-1:-1:-1;;;;;133782:17:0;;-1:-1:-1;;;;;;;;;;;133782:17:0;:25;;133806:1;133782:25;;;133802:1;133782:25;-1:-1:-1;;;;;133740:16:0;;-1:-1:-1;;;;;;;;;;;133740:16:0;:24;;133763:1;133740:24;;;133759:1;133740:24;133739:69;:112;133728:123;;;;133862:8;70710:42;-1:-1:-1;;;;;133959:17:0;:9;-1:-1:-1;;;;;133959:17:0;;:25;;133983:1;133959:25;;;133979:1;133959:25;-1:-1:-1;;;;;133916:17:0;;-1:-1:-1;;;;;;;;;;;133916:17:0;:25;;133940:1;133916:25;;;133936:1;133916:25;-1:-1:-1;;;;;133874:16:0;;-1:-1:-1;;;;;;;;;;;133874:16:0;:24;;133897:1;133874:24;;;133893:1;133874:24;133873:69;:112;133862:123;;;;134000:1;:6;;134005:1;134000:6;:16;;;;134010:1;:6;;134015:1;134010:6;134000:16;133996:55;;;134033:7;;;;133996:55;134063:54;-1:-1:-1;;;;;134063:26:0;;72839:42;134110:6;134063:54;:26;:54;:::i;:::-;134128;;;-1:-1:-1;;;134128:54:0;;-1:-1:-1;;134158:5:0;;;134128:54;;;;;;;;;;134165:5;;;134128:54;;;;;;;;;;;;;;134180:1;134128:54;;;;;;;;72839:42;;134128:29;;:54;;;;;;;;;;;134180:1;72839:42;134128:54;;;5:2:-1;;;;30:1;27;20:12;134198:699:0;134355:8;-1:-1:-1;;;;;134495:17:0;;70799:42;134495:17;:25;;134519:1;134495:25;;;134515:1;134495:25;-1:-1:-1;;;;;134452:17:0;;70710:42;134452:17;:25;;134476:1;134452:25;;;134472:1;134452:25;-1:-1:-1;;;;;134409:17:0;;-1:-1:-1;;;;;;;;;;;134409:17:0;:25;;134433:1;134409:25;;;134429:1;134409:25;-1:-1:-1;;;;;134367:16:0;;-1:-1:-1;;;;;;;;;;;134367:16:0;:24;;134390:1;134367:24;;;134386:1;134367:24;134366:69;:112;:155;134355:166;;;;134532:8;70799:42;-1:-1:-1;;;;;134672:17:0;:9;-1:-1:-1;;;;;134672:17:0;;:25;;134696:1;134672:25;;;134692:1;134672:25;-1:-1:-1;;;;;134629:17:0;;70710:42;134629:17;:25;;134653:1;134629:25;;;134649:1;134629:25;-1:-1:-1;;;;;134586:17:0;;-1:-1:-1;;;;;;;;;;;134586:17:0;:25;;134610:1;134586:25;;;134606:1;134586:25;-1:-1:-1;;;;;134544:16:0;;-1:-1:-1;;;;;;;;;;;134544:16:0;:24;;134567:1;134544:24;;;134563:1;134544:24;134543:69;:112;:155;134532:166;;;;134713:1;:6;;134718:1;134713:6;:16;;;;134723:1;:6;;134728:1;134723:6;134713:16;134709:55;;;134746:7;;;;134709:55;134776:51;-1:-1:-1;;;;;134776:26:0;;72930:42;134820:6;134776:51;:26;:51;:::i;:::-;134838;;;-1:-1:-1;;;134838:51:0;;-1:-1:-1;;134865:5:0;;;134838:51;;;;;;;;;;134872:5;;;134838:51;;;;;;;;;;;;;;134887:1;134838:51;;;;;;;;72930:42;;134838:26;;:51;;;;;;;;;;;134887:1;72930:42;134838:51;;;5:2:-1;;;;30:1;27;20:12;134905:717:0;135068:8;-1:-1:-1;;;;;135208:17:0;;70888:42;135208:17;:25;;135232:1;135208:25;;;135228:1;135208:25;-1:-1:-1;;;;;135165:17:0;;70710:42;135165:17;:25;;135189:1;135165:25;;;135185:1;135165:25;-1:-1:-1;;;;;135122:17:0;;-1:-1:-1;;;;;;;;;;;135122:17:0;:25;;135146:1;135122:25;;;135142:1;135122:25;-1:-1:-1;;;;;135080:16:0;;-1:-1:-1;;;;;;;;;;;135080:16:0;:24;;135103:1;135080:24;;;135099:1;135080:24;135079:69;:112;:155;135068:166;;;;135245:8;70888:42;-1:-1:-1;;;;;135385:17:0;:9;-1:-1:-1;;;;;135385:17:0;;:25;;135409:1;135385:25;;;135405:1;135385:25;-1:-1:-1;;;;;135342:17:0;;70710:42;135342:17;:25;;135366:1;135342:25;;;135362:1;135342:25;-1:-1:-1;;;;;135299:17:0;;-1:-1:-1;;;;;;;;;;;135299:17:0;:25;;135323:1;135299:25;;;135319:1;135299:25;-1:-1:-1;;;;;135257:16:0;;-1:-1:-1;;;;;;;;;;;135257:16:0;:24;;135280:1;135257:24;;;135276:1;135257:24;135256:69;:112;:155;135245:166;;;;135426:1;:6;;135431:1;135426:6;:16;;;;135436:1;:6;;135441:1;135436:6;135426:16;135422:55;;;135459:7;;;;135422:55;135489:57;-1:-1:-1;;;;;135489:26:0;;73027:42;135539:6;135489:57;:26;:57;:::i;:::-;135557;;;-1:-1:-1;;;135557:57:0;;-1:-1:-1;;135590:5:0;;;135557:57;;;;;;;;;;135597:5;;;135557:57;;;;;;;;;;;;;;135612:1;135557:57;;;;;;;;73027:42;;135557:32;;:57;;;;;;;;;;;135612:1;73027:42;135557:57;;;5:2:-1;;;;30:1;27;20:12;135630:723:0;135795:8;-1:-1:-1;;;;;135935:17:0;;70977:42;135935:17;:25;;135959:1;135935:25;;;135955:1;135935:25;-1:-1:-1;;;;;135892:17:0;;70710:42;135892:17;:25;;135916:1;135892:25;;;135912:1;135892:25;-1:-1:-1;;;;;135849:17:0;;-1:-1:-1;;;;;;;;;;;135849:17:0;:25;;135873:1;135849:25;;;135869:1;135849:25;-1:-1:-1;;;;;135807:16:0;;-1:-1:-1;;;;;;;;;;;135807:16:0;:24;;135830:1;135807:24;;;135826:1;135807:24;135806:69;:112;:155;135795:166;;;;135972:8;70977:42;-1:-1:-1;;;;;136112:17:0;:9;-1:-1:-1;;;;;136112:17:0;;:25;;136136:1;136112:25;;;136132:1;136112:25;-1:-1:-1;;;;;136069:17:0;;70710:42;136069:17;:25;;136093:1;136069:25;;;136089:1;136069:25;-1:-1:-1;;;;;136026:17:0;;-1:-1:-1;;;;;;;;;;;136026:17:0;:25;;136050:1;136026:25;;;136046:1;136026:25;-1:-1:-1;;;;;135984:16:0;;-1:-1:-1;;;;;;;;;;;135984:16:0;:24;;136007:1;135984:24;;;136003:1;135984:24;135983:69;:112;:155;135972:166;;;;136153:1;:6;;136158:1;136153:6;:16;;;;136163:1;:6;;136168:1;136163:6;136153:16;136149:55;;;136186:7;;;;136149:55;136216:59;-1:-1:-1;;;;;136216:26:0;;73126:42;136268:6;136216:59;:26;:59;:::i;:::-;136286;;;-1:-1:-1;;;136286:59:0;;-1:-1:-1;;136321:5:0;;;136286:59;;;;;;;;;;136328:5;;;136286:59;;;;;;;;;;;;;;136343:1;136286:59;;;;;;;;73126:42;;136286:34;;:59;;;;;;;;;;;136343:1;73126:42;136286:59;;;5:2:-1;;;;30:1;27;20:12;140761:921:0;140928:17;:9;-1:-1:-1;;;;;140928:15:0;;:17::i;:::-;140923:380;;140992:41;;;-1:-1:-1;;;140992:41:0;;-1:-1:-1;;;;;140992:41:0;;;;;;;;140962:27;;74956:42;;140992:30;;:41;;;;;;;;;;;;;;;74956:42;140992:41;;;5:2:-1;;;;30:1;27;20:12;5:2;140992:41:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;140992:41:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;140992:41:0;;-1:-1:-1;141048:57:0;-1:-1:-1;;;;;141048:26:0;;140992:41;141098:6;141048:57;:26;:57;:::i;:::-;141120:12;-1:-1:-1;;;;;141120:17:0;;141138:6;141120:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;141120:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;141120:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;141160:110:0;;-1:-1:-1;141182:12:0;141197:9;141208:54;-1:-1:-1;;;;;141208:39:0;;141256:4;141208:54;:39;:54;:::i;:::-;141264:5;141160:14;:110::i;:::-;141285:7;;;140923:380;141320:17;:9;-1:-1:-1;;;;;141320:15:0;;:17::i;:::-;141315:360;;141382:41;;;-1:-1:-1;;;141382:41:0;;-1:-1:-1;;;;;141382:41:0;;;;;;;;141354:25;;74956:42;;141382:30;;:41;;;;;;;;;;;;;;;74956:42;141382:41;;;5:2:-1;;;;30:1;27;20:12;5:2;141382:41:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;141382:41:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;141382:41:0;;-1:-1:-1;141438:60:0;141453:9;141382:41;141484:6;141492:5;141438:14;:60::i;:::-;-1:-1:-1;;;;;141513:17:0;;;141531:52;141513:17;141577:4;141531:52;:37;:52;:::i;:::-;141513:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;141513:71:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;141513:71:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;141599:43:0;;-1:-1:-1;;;;;;141599:28:0;;141636:4;141599:43;:28;:43;:::i;:::-;;141657:7;;;141690:646;-1:-1:-1;;;;;141852:16:0;;-1:-1:-1;;;;;;;;;;;141852:16:0;141848:275;;;141885:49;-1:-1:-1;;;;;141885:26:0;;70444:42;141927:6;141885:49;:26;:49;:::i;:::-;141949:32;;;-1:-1:-1;;;141949:32:0;;141967:4;141949:32;;;;;;;;;;;;70444:42;;141949:9;;:32;;;;;-1:-1:-1;;141949:32:0;;;;;;;-1:-1:-1;70444:42:0;141949:32;;;5:2:-1;;;;30:1;27;20:12;5:2;141949:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;141996:94:0;;-1:-1:-1;70444:42:0;;-1:-1:-1;142025:9:0;;-1:-1:-1;142036:46:0;70444:42;142076:4;142036:46;:31;:46;:::i;:::-;142084:5;141996:14;:94::i;:::-;142105:7;;141848:275;-1:-1:-1;;;;;142139:16:0;;-1:-1:-1;;;;;;;;;;;142139:16:0;142135:194;;;142172:54;142187:9;70444:42;142212:6;142220:5;142172:14;:54::i;:::-;142266:29;;;-1:-1:-1;;;142266:29:0;;142259:4;142266:29;;;;;;;;70444:42;;142241:9;;142259:4;;70444:42;;142266:14;;:29;;;;;;;;;;;;;;70444:42;142266:29;;;5:2:-1;;;;30:1;27;20:12;5:2;142266:29:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;142266:29:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;142266:29:0;142241:55;;;-1:-1:-1;;;;;;142241:55:0;;;;;;;-1:-1:-1;;;;;142241:55:0;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;142241:55:0;;;;;;;-1:-1:-1;142241:55:0;;;;5:2:-1;;;;30:1;27;20:12;5:2;142241:55:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;142241:55:0;;;;142311:7;;142344:800;142507:17;:9;-1:-1:-1;;;;;142507:15:0;;:17::i;:::-;142502:362;;142563:37;;;-1:-1:-1;;;142563:37:0;;-1:-1:-1;;;;;142563:37:0;;;;;;;;142541:19;;75067:42;;142563:26;;:37;;;;;;;;;;;;;;;75067:42;142563:37;;;5:2:-1;;;;30:1;27;20:12;5:2;142563:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;142563:37:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;142563:37:0;142642:11;;;-1:-1:-1;;;142642:11:0;;;;142563:37;;-1:-1:-1;142615:47:0;;73702:42;;142642:9;;:11;;;;;142563:37;;142642:11;;;;;;;73702:42;142642:11;;;5:2:-1;;;;30:1;27;20:12;5:2;142642:11:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;142642:11:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;142642:11:0;-1:-1:-1;;;;;142615:26:0;;;142655:6;142615:47;:26;:47;:::i;:::-;142677:37;;;-1:-1:-1;;;142677:37:0;;-1:-1:-1;;;;;142677:37:0;;;;;;;;;;;;142709:4;142677:37;;;;;;73702:42;;142677:12;;:37;;;;;-1:-1:-1;;142677:37:0;;;;;;;-1:-1:-1;73702:42:0;142677:37;;;5:2:-1;;;;30:1;27;20:12;5:2;142677:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;142677:37:0;;;;142729:102;142751:8;142762:9;142773:50;142817:4;142780:8;-1:-1:-1;;;;;142773:35:0;;;:50;;;;:::i;142502:362::-;142881:17;:9;-1:-1:-1;;;;;142881:15:0;;:17::i;:::-;142876:261;;142935:37;;;-1:-1:-1;;;142935:37:0;;-1:-1:-1;;;;;142935:37:0;;;;;;;;142915:17;;75067:42;;142935:26;;:37;;;;;;;;;;;;;;;75067:42;142935:37;;;5:2:-1;;;;30:1;27;20:12;5:2;142935:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;142935:37:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;142935:37:0;;-1:-1:-1;142987:56:0;143002:9;142935:37;143029:6;143037:5;142987:14;:56::i;:::-;143072:31;;;-1:-1:-1;;;143072:31:0;;143097:4;143072:31;;;;;;-1:-1:-1;;;;;143058:13:0;;;;;;;143072:16;;:31;;;;;;;;;;;;;;143058:13;143072:31;;;5:2:-1;;;;30:1;27;20:12;5:2;143072:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;143072:31:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;143072:31:0;143058:46;;;-1:-1:-1;;;;;;143058:46:0;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;143058:46:0;;;;;;;-1:-1:-1;143058:46:0;;;;5:2:-1;;;;30:1;27;20:12;5:2;143058:46:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;143058:46:0;;;;143119:7;;;143152:694;143312:20;74032:42;143335:23;143373:17;-1:-1:-1;;;;;143373:15:0;;;:17::i;:::-;:44;;143408:9;143373:44;;;70186:1;143373:44;143432:17;:9;-1:-1:-1;;;;;143432:15:0;;:17::i;:::-;:44;;143467:9;143432:44;;;70186:1;143432:44;143335:152;;;;;;;;;;;;;-1:-1:-1;;;;;143335:152:0;-1:-1:-1;;;;;143335:152:0;;;;;;-1:-1:-1;;;;;143335:152:0;-1:-1:-1;;;;;143335:152:0;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;143335:152:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;143335:152:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;143335:152:0;;-1:-1:-1;143498:54:0;-1:-1:-1;;;;;143498:26:0;;143335:152;143545:6;143498:54;:26;:54;:::i;:::-;143563:9;-1:-1:-1;;;;;143563:14:0;;143584:17;:9;-1:-1:-1;;;;;143584:15:0;;:17::i;:::-;:30;;143613:1;143584:30;;;143604:6;143584:30;143630:17;:9;-1:-1:-1;;;;;143630:15:0;;:17::i;:::-;:44;;143665:9;143630:44;;;70186:1;143630:44;143689:17;:9;-1:-1:-1;;;;;143689:15:0;;:17::i;:::-;:44;;143724:9;143689:44;;;70186:1;143689:44;143563:275;;;-1:-1:-1;;;;;;143563:275:0;;;;;;;-1:-1:-1;;;;;143563:275:0;;;;;;;;;;;;;;;;;;;;;143769:1;143563:275;;;;143785:42;143563:275;;;;;;;;;;;;;;;;;;;;;;;;;5:2:-1;;;;30:1;27;20:12;5:2;143563:275:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;143563:275:0;;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;;143152:694:0:o;150934:288::-;151090:124;151129:9;151153;151177:6;151198:5;151090:24;:124::i;151230:309::-;151389:142;151427:9;-1:-1:-1;;;;;;;;;;;151470:9:0;151494:6;151515:5;151389:23;:142::i;151547:308::-;151706:141;151744:9;-1:-1:-1;;;;;;;;;;;151786:9:0;151810:6;151831:5;151706:23;:141::i;151863:310::-;152023:142;152061:9;-1:-1:-1;;;;;;;;;;;152104:9:0;152128:6;152149:5;152023:23;:142::i;136361:703::-;136520:8;-1:-1:-1;;;;;136660:16:0;;71065:42;136660:16;:24;;136683:1;136660:24;;;136679:1;136660:24;-1:-1:-1;;;;;136617:17:0;;70710:42;136617:17;:25;;136641:1;136617:25;;;136637:1;136617:25;-1:-1:-1;;;;;136574:17:0;;-1:-1:-1;;;;;;;;;;;136574:17:0;:25;;136598:1;136574:25;;;136594:1;136574:25;-1:-1:-1;;;;;136532:16:0;;-1:-1:-1;;;;;;;;;;;136532:16:0;:24;;136555:1;136532:24;;;136551:1;136532:24;136531:69;:112;:154;136520:165;;;;136696:8;71065:42;-1:-1:-1;;;;;136836:16:0;:9;-1:-1:-1;;;;;136836:16:0;;:24;;136859:1;136836:24;;;136855:1;136836:24;-1:-1:-1;;;;;136793:17:0;;70710:42;136793:17;:25;;136817:1;136793:25;;;136813:1;136793:25;-1:-1:-1;;;;;136750:17:0;;-1:-1:-1;;;;;;;;;;;136750:17:0;:25;;136774:1;136750:25;;;136770:1;136750:25;-1:-1:-1;;;;;136708:16:0;;-1:-1:-1;;;;;;;;;;;136708:16:0;:24;;136731:1;136708:24;;;136727:1;136708:24;136707:69;:112;:154;136696:165;;;;136876:1;:6;;136881:1;136876:6;:16;;;;136886:1;:6;;136891:1;136886:6;136876:16;136872:55;;;136909:7;;;;136872:55;136939:53;-1:-1:-1;;;;;136939:26:0;;73219:42;136985:6;136939:53;:26;:53;:::i;:::-;137003;;;-1:-1:-1;;;137003:53:0;;-1:-1:-1;;137032:5:0;;;137003:53;;;;;;;;;;137039:5;;;137003:53;;;;;;;;;;;;;;137054:1;137003:53;;;;;;;;73219:42;;137003:28;;:53;;;;;;;;;;;137054:1;73219:42;137003:53;;;5:2:-1;;;;30:1;27;20:12;137815:537:0;137977:8;-1:-1:-1;;;;;138034:17:0;;71245:42;138034:17;:25;;138058:1;138034:25;;;138054:1;138034:25;-1:-1:-1;;;;;137989:19:0;;71156:42;137989:19;:27;;138015:1;137989:27;;;138011:1;137989:27;137988:72;137977:83;;;-1:-1:-1;138071:8:0;71245:42;-1:-1:-1;;;;;138128:17:0;;;:25;;138152:1;138128:25;;;138148:1;138128:25;-1:-1:-1;;;;;138083:19:0;;71156:42;138083:19;:27;;138109:1;138083:27;;;138105:1;138083:27;138082:72;138071:83;;;;138169:1;:6;;138174:1;138169:6;:16;;;;138179:1;:6;;138184:1;138179:6;138169:16;138165:55;;;138202:7;;;;138165:55;138232:56;-1:-1:-1;;;;;138232:26:0;;73315:42;138281:6;138232:56;:26;:56;:::i;:::-;138299:45;;;-1:-1:-1;;;138299:45:0;;-1:-1:-1;;138320:5:0;;;138299:45;;;;;;;;;;138327:5;;;138299:45;;;;;;;;;;;;;;138342:1;138299:45;;;;;;;;73315:42;;138299:20;;:45;;;;;;;;;;;138342:1;73315:42;138299:45;;;5:2:-1;;;;30:1;27;20:12;138360:613:0;138520:8;-1:-1:-1;;;;;138618:17:0;;71423:42;138618:17;:25;;138642:1;138618:25;;;138638:1;138618:25;-1:-1:-1;;;;;138575:17:0;;71245:42;138575:17;:25;;138599:1;138575:25;;;138595:1;138575:25;-1:-1:-1;;;;;138532:17:0;;71334:42;138532:17;:25;;138556:1;138532:25;;;138552:1;138532:25;138531:70;:113;138520:124;;;;138655:8;71423:42;-1:-1:-1;;;;;138753:17:0;:9;-1:-1:-1;;;;;138753:17:0;;:25;;138777:1;138753:25;;;138773:1;138753:25;-1:-1:-1;;;;;138710:17:0;;71245:42;138710:17;:25;;138734:1;138710:25;;;138730:1;138710:25;-1:-1:-1;;;;;138667:17:0;;71334:42;138667:17;:25;;138691:1;138667:25;;;138687:1;138667:25;138666:70;:113;138655:124;;;;138794:1;:6;;138799:1;138794:6;:16;;;;138804:1;:6;;138809:1;138804:6;138794:16;138790:55;;;138827:7;;;;138790:55;138857:54;-1:-1:-1;;;;;138857:26:0;;73409:42;138904:6;138857:54;:26;:54;:::i;:::-;138922:43;;;-1:-1:-1;;;138922:43:0;;-1:-1:-1;;138941:5:0;;;138922:43;;;;;;;;;;138948:5;;;138922:43;;;;;;;;;;;;;;138963:1;138922:43;;;;;;;;73409:42;;138922:18;;:43;;;;;;;;;;;138963:1;73409:42;138922:43;;;5:2:-1;;;;30:1;27;20:12;139606:280:0;139767:55;-1:-1:-1;;;;;139767:26:0;;74253:42;139815:6;139767:55;:26;:55;:::i;:::-;139833:45;;;-1:-1:-1;;;139833:45:0;;-1:-1:-1;;;;;139833:45:0;;;;;;;;;;;;;;;;;;;;;74253:42;;139833:15;;:45;;;;;-1:-1:-1;;139833:45:0;;;;;;;-1:-1:-1;74253:42:0;139833:45;;;5:2:-1;;;;30:1;27;20:12;5:2;139833:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;137072:380:0;137228:50;-1:-1:-1;;;;;137228:26:0;;73593:42;137271:6;137228:50;:26;:50;:::i;:::-;137289:155;;;-1:-1:-1;;;137289:155:0;;-1:-1:-1;;;;;137289:155:0;;;;;;;;;;;;;;;;;;;137409:1;137289:155;;;;;;137431:2;137425:3;:8;137289:155;;;;;;73593:42;;137289:18;;:155;;;;;;;;;;;;;;;;;;73593:42;137289:155;;;5:2:-1;;;;30:1;27;20:12;5:2;137289:155:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;137289:155:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;137460:347:0;137622:49;-1:-1:-1;;;;;137622:26:0;;74346:42;137664:6;137622:49;:26;:49;:::i;:::-;137682:117;;;-1:-1:-1;;;137682:117:0;;-1:-1:-1;;;;;137682:117:0;;;;;;;;;;;;;;;;;;;137783:4;137682:117;;;;;;74346:42;;137682:9;;:117;;;;;;;;;;;;;;-1:-1:-1;74346:42:0;137682:117;;;5:2:-1;;;;30:1;27;20:12;138981:617:0;139141:8;-1:-1:-1;;;;;139241:17:0;;71512:42;139241:17;:25;;139265:1;139241:25;;;139261:1;139241:25;-1:-1:-1;;;;;139198:17:0;;71245:42;139198:17;:25;;139222:1;139198:25;;;139218:1;139198:25;-1:-1:-1;;;;;139153:19:0;;71156:42;139153:19;:27;;139179:1;139153:27;;;139175:1;139153:27;139152:72;:115;139141:126;;;;139278:8;71512:42;-1:-1:-1;;;;;139378:17:0;:9;-1:-1:-1;;;;;139378:17:0;;:25;;139402:1;139378:25;;;139398:1;139378:25;-1:-1:-1;;;;;139335:17:0;;71245:42;139335:17;:25;;139359:1;139335:25;;;139355:1;139335:25;-1:-1:-1;;;;;139290:19:0;;71156:42;139290:19;:27;;139316:1;139290:27;;;139312:1;139290:27;139289:72;:115;139278:126;;;;139419:1;:6;;139424:1;139419:6;:16;;;;139429:1;:6;;139434:1;139429:6;139419:16;139415:55;;;139452:7;;;;139415:55;139482:54;-1:-1:-1;;;;;139482:26:0;;73503:42;139529:6;139482:54;:26;:54;:::i;:::-;139547:43;;;-1:-1:-1;;;139547:43:0;;-1:-1:-1;;139566:5:0;;;139547:43;;;;;;;;;;139573:5;;;139547:43;;;;;;;;;;;;;;139588:1;139547:43;;;;;;;;73503:42;;139547:18;;:43;;;;;;;;;;;139588:1;73503:42;139547:43;;;5:2:-1;;;;30:1;27;20:12;153133:220:0;153289:56;153306:9;153317;153328:6;153336:5;153343:1;153289:16;:56::i;153361:220::-;153517:56;153534:9;153545;153556:6;153564:5;153571:1;153517:16;:56::i;153589:220::-;153745:56;153762:9;153773;153784:6;153792:5;153799:1;153745:16;:56::i;144997:354::-;145150:193;145177:9;145201;145225:6;145246:5;-1:-1:-1;;;;;;145150:12:0;:193::i;145359:354::-;145512:193;145539:9;145563;145587:6;145608:5;-1:-1:-1;;;;;;145512:12:0;:193::i;145721:354::-;145874:193;145901:9;145925;145949:6;145970:5;-1:-1:-1;;;;;;145874:12:0;:193::i;146083:333::-;146236:172;146263:9;146287;146311:6;146332:5;146352:45;146376:9;146387;146352:23;:45::i;:::-;146236:12;:172::i;143854:305::-;144013:56;144030:9;70186:1;144055:6;144063:5;144013:16;:56::i;:::-;144080:71;70186:1;144111:9;144122:21;144145:5;144080:16;:71::i;144167:294::-;144326:47;144343:9;-1:-1:-1;;;;;;;;;;;144359:6:0;144367:5;144326:16;:47::i;:::-;144417:28;;;-1:-1:-1;;;144417:28:0;;144439:4;144417:28;;;;;;144384:69;;-1:-1:-1;;;;;;;;;;;70532:42:0;144406:9;;70532:42;;144417:13;;:28;;;;;;;;;;;;;;;70532:42;144417:28;;;5:2:-1;;;;30:1;27;20:12;5:2;144417:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;144417:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;144417:28:0;144447:5;144384:16;:69::i;144469:298::-;144629:48;144646:9;-1:-1:-1;;;;;;;;;;;144663:6:0;144671:5;144629:16;:48::i;:::-;144722:29;;;-1:-1:-1;;;144722:29:0;;144745:4;144722:29;;;;;;144688:71;;-1:-1:-1;;;;;;;;;;;70621:42:0;144711:9;;70621:42;;144722:14;;:29;;;;;;;;;;;;;;;70621:42;144722:29;;;5:2:-1;;;;30:1;27;20:12;12663:181:0;12721:7;12753:5;;;12777:6;;;;12769:46;;;;;-1:-1:-1;;;12769:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;12835:1;-1:-1:-1;12663:181:0;;;;;:::o;41621:166::-;41672:4;-1:-1:-1;;;;;41697:39:0;;;;:81;;-1:-1:-1;;;;;;41740:38:0;;38793:42;41740:38;41689:90;41621:166;-1:-1:-1;;41621:166:0:o;39201:617::-;39312:11;39308:50;;39340:7;;39308:50;39374:12;39380:5;39374;:12::i;:::-;39370:441;;;-1:-1:-1;;;;;39411:18:0;;39419:10;39411:18;:41;;;;;39446:6;39433:9;:19;;39411:41;39403:97;;;;-1:-1:-1;;;39403:97:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;39519:19:0;;39533:4;39519:19;39515:97;;39559:37;;-1:-1:-1;;;;;39559:29:0;;;:37;;;;;39589:6;;39559:37;;;;39589:6;39559:29;:37;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;39559:37:0;39515:97;39642:6;39630:9;:18;39626:101;;;39669:10;:42;39689:21;:9;39703:6;39689:21;:13;:21;:::i;:::-;39669:42;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;39370:441:0;39759:40;-1:-1:-1;;;;;39759:22:0;;39782:4;39788:2;39792:6;39759:40;:22;:40;:::i;40803:228::-;40881:7;40905:12;40911:5;40905;:12::i;:::-;40901:123;;;-1:-1:-1;;;;;;40941:11:0;;;40934:18;;40901:123;40992:5;-1:-1:-1;;;;;40992:15:0;;41008:3;40992:20;;;;;;;;;;;;;-1:-1:-1;;;;;40992:20:0;-1:-1:-1;;;;;40992:20:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;40992:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;40992:20:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;40992:20:0;;-1:-1:-1;40985:27:0;;14035:471;14093:7;14338:6;14334:47;;-1:-1:-1;14368:1:0;14361:8;;14334:47;14405:5;;;14409:1;14405;:5;:1;14429:5;;;;;:10;14421:56;;;;-1:-1:-1;;;14421:56:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14974:132;15032:7;15059:39;15063:1;15066;15059:39;;;;;;;;;;;;;;;;;:3;:39::i;38845:348::-;38931:4;38952:11;38948:55;;-1:-1:-1;38987:4:0;38980:11;;38948:55;39019:12;39025:5;39019;:12::i;:::-;39015:171;;;39048:37;;-1:-1:-1;;;;;39048:29:0;;;:37;;;;;39078:6;;39048:37;;;;39078:6;39048:29;:37;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;39048:37:0;39015:171;;;39118:30;-1:-1:-1;;;;;39118:18:0;;39137:2;39141:6;39118:30;:18;:30;:::i;:::-;-1:-1:-1;39170:4:0;39015:171;38845:348;;;;;:::o;40277:518::-;40370:12;40376:5;40370;:12::i;:::-;40365:423;;40403:11;40399:101;;40435:24;-1:-1:-1;;;;;40435:17:0;;40453:2;40457:1;40435:24;:17;:24;:::i;:::-;40478:7;;40399:101;40536:34;;;-1:-1:-1;;;40536:34:0;;40560:4;40536:34;;;;-1:-1:-1;;;;;40536:34:0;;;;;;;;;40516:17;;40536:15;;;;;:34;;;;;;;;;;;;;;:15;:34;;;5:2:-1;;;;30:1;27;20:12;5:2;40536:34:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;40536:34:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;40536:34:0;;-1:-1:-1;40589:18:0;;;40585:192;;;40632:13;;40628:86;;40670:24;-1:-1:-1;;;;;40670:17:0;;40688:2;40692:1;40670:24;:17;:24;:::i;:::-;40732:29;-1:-1:-1;;;;;40732:17:0;;40750:2;40754:6;40732:29;:17;:29;:::i;40365:423::-;40277:518;;;:::o;149458:1003::-;149623:20;149660:17;:9;-1:-1:-1;;;;;149660:15:0;;:17::i;:::-;149656:78;;;-1:-1:-1;;;;;;;;;;;;;;;;149694:12:0;;149713:6;149694:28;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;149694:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;149694:28:0;;;;;149656:78;149746:20;149769:17;:9;-1:-1:-1;;;;;149769:15:0;;:17::i;:::-;:36;;149796:9;149769:36;;;-1:-1:-1;;;;;;;;;;;149769:36:0;149746:59;;149816:18;149837:17;:9;-1:-1:-1;;;;;149837:15:0;;:17::i;:::-;:36;;149864:9;149837:36;;;-1:-1:-1;;;;;;;;;;;149837:36:0;149914:45;;;-1:-1:-1;;;149914:45:0;;-1:-1:-1;;;;;149914:45:0;;;;;;;;;;;;;;;149816:57;;-1:-1:-1;149884:27:0;;74148:42;;149914:17;;:45;;;;;;;;;;;;;;74148:42;149914:45;;;5:2:-1;;;;30:1;27;20:12;5:2;149914:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;149914:45:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;149914:45:0;;-1:-1:-1;149985:54:0;-1:-1:-1;;;;;149985:18:0;;150004:13;150019:11;150032:6;149985:54;:18;:54;:::i;:::-;149970:69;-1:-1:-1;150052:58:0;-1:-1:-1;;;;;150052:31:0;;150092:8;150103:6;150052:58;:31;:58;:::i;:::-;;150175:11;-1:-1:-1;;;;;150159:29:0;150141:13;-1:-1:-1;;;;;150125:31:0;:63;150121:227;;;150205:49;;;-1:-1:-1;;;150205:49:0;;150219:1;150205:49;;;;;;;;;;;;150244:4;150205:49;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;150205:13:0;;;;;:49;;;;;;;;;;;150219:1;150205:13;:49;;;5:2:-1;;;;30:1;27;20:12;5:2;150205:49:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;150205:49:0;;;;150121:227;;;150287:49;;;-1:-1:-1;;;150287:49:0;;;;;;;;150315:1;150287:49;;;;;;150326:4;150287:49;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;150287:13:0;;;;;:49;;;;;;;;;;;150315:1;150287:13;:49;;;5:2:-1;;;;30:1;27;20:12;5:2;150287:49:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;150287:49:0;;;;150121:227;150364:17;:9;-1:-1:-1;;;;;150364:15:0;;:17::i;:::-;150360:94;;;150412:29;;;-1:-1:-1;;;150412:29:0;;150435:4;150412:29;;;;;;-1:-1:-1;;;;;;;;;;;70357:42:0;150398:13;;70357:42;;150412:14;;:29;;;;;;;;;;;;;;70357:42;150412:29;;;5:2:-1;;;;30:1;27;20:12;5:2;150412:29:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;150412:29:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;150412:29:0;150398:44;;;-1:-1:-1;;;;;;150398:44:0;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;150398:44:0;;;;;;;-1:-1:-1;150398:44:0;;;;5:2:-1;;;;30:1;27;20:12;5:2;150398:44:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;150398:44:0;;;;150360:94;149458:1003;;;;;;;;;:::o;150469:457::-;150658:260;150697:8;150720:9;150744:143;150787:9;150815:8;150842:6;150867:5;150744:24;:143::i;:::-;150902:5;150658:24;:260::i;152181:944::-;152369:22;74599:42;152394:38;152455:17;-1:-1:-1;;;;;152455:15:0;;;:17::i;:::-;:36;;152482:9;152455:36;;;-1:-1:-1;;;;;;;;;;;152455:36:0;152515:17;:9;-1:-1:-1;;;;;152515:15:0;;:17::i;:::-;:36;;152542:9;152515:36;;;-1:-1:-1;;;;;;;;;;;152515:36:0;152567:9;152579:1;152567:13;152394:197;;;;;;;;;;;;;-1:-1:-1;;;;;152394:197:0;-1:-1:-1;;;;;152394:197:0;;;;;;-1:-1:-1;;;;;152394:197:0;-1:-1:-1;;;;;152394:197:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;152394:197:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;152394:197:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;152394:197:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;152394:197:0;;;;;;;;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;219:3;213:10;331:9;325:2;311:12;307:21;289:16;285:44;282:59;-1:-1;;;247:12;244:29;233:116;230:2;;;362:1;359;352:12;230:2;373:25;;-1:-1;152394:197:0;;421:4:-1;412:14;;;;152394:197:0;;;;;412:14:-1;152394:197:0;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;152394:197:0;;;;;;;;;;;152369:222;;152608:17;:9;-1:-1:-1;;;;;152608:15:0;;:17::i;:::-;152604:78;;;-1:-1:-1;;;;;;;;;;;;;;;;152642:12:0;;152661:6;152642:28;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;152642:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;152642:28:0;;;;;152604:78;152694:81;152750:5;152756:9;152750:16;;;;;;;;;;;;;;152768:6;152695:17;:9;-1:-1:-1;;;;;152695:15:0;;:17::i;:::-;:36;;152722:9;152695:36;;;-1:-1:-1;;;;;;;;;;;152695:36:0;-1:-1:-1;;;;;152694:55:0;;:81;;:55;:81;:::i;:::-;152800:5;152806:9;152800:16;;;;;;;;;;;;;;-1:-1:-1;;;;;152786:49:0;;152850:17;:9;-1:-1:-1;;;;;152850:15:0;;:17::i;:::-;:36;;152877:9;152850:36;;;-1:-1:-1;;;;;;;;;;;152850:36:0;152901:6;152922:17;:9;-1:-1:-1;;;;;152922:15:0;;:17::i;:::-;:36;;152949:9;152922:36;;;-1:-1:-1;;;;;;;;;;;152922:36:0;152973:1;-1:-1:-1;;152786:225:0;;;;;;;;;;;;;-1:-1:-1;;;;;152786:225:0;-1:-1:-1;;;;;152786:225:0;;;;;;;;;;;-1:-1:-1;;;;;152786:225:0;-1:-1:-1;;;;;152786:225:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;152786:225:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;152786:225:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;153028:17:0;;-1:-1:-1;;;;;;153028:15:0;;;:17::i;:::-;153024:94;;;153076:29;;;-1:-1:-1;;;153076:29:0;;153099:4;153076:29;;;;;;-1:-1:-1;;;;;;;;;;;70357:42:0;153062:13;;70357:42;;153076:14;;:29;;;;;;;;;;;;;;70357:42;153076:29;;;5:2:-1;;;;30:1;27;20:12;5:2;153076:29:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;153076:29:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;153076:29:0;153062:44;;;-1:-1:-1;;;;;;153062:44:0;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;153062:44:0;;;;;;;-1:-1:-1;153062:44:0;;;;5:2:-1;;;;30:1;27;20:12;146424:1707:0;146680:16;;;146694:1;146680:16;;;;;;;;;146631:6;;146650:27;;146680:16;;;;;;;105:10:-1;146680:16:0;88:34:-1;136:17;;-1:-1;146680:16:0;146650:46;;146723:9;146707:10;146718:1;146707:13;;;;;;;;;;;;;:25;;;;;146750:17;:9;-1:-1:-1;;;;;146750:15:0;;:17::i;:::-;146745:712;;146784:21;71874:42;-1:-1:-1;;;;;146808:36:0;;146863:9;146891:34;146944:10;146987:1;146973:16;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;146973:16:0;;146808:196;;;;;;;;;;;;;-1:-1:-1;;;;;146808:196:0;-1:-1:-1;;;;;146808:196:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;146808:196:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;146808:196:0;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;146808:196:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;146808:196:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;146808:196:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;146808:196:0;;;;;;;;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;213:10;;-1:-1;;;244:29;;285:43;;;282:58;-1:-1;233:115;230:2;;;361:1;358;351:12;230:2;372:25;;-1:-1;146808:196:0;;420:4:-1;411:14;;;;146808:196:0;;;;;411:14:-1;146808:196:0;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;146808:196:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;146808:196:0;;-1:-1:-1;146784:220:0;;-1:-1:-1;147021:62:0;;-1:-1:-1;;;;;;;147021:26:0;;71640:42;147076:6;147021:62;:26;:62;:::i;:::-;71640:42;-1:-1:-1;;;;;147113:37:0;;147169:9;147197:12;70089:42;147266:4;-1:-1:-1;;147320:1:0;147340:42;147401:2;147422:8;147113:332;;;;;;;;;;;;;-1:-1:-1;;;;;147113:332:0;-1:-1:-1;;;;;147113:332:0;;;;;;;;;;;-1:-1:-1;;;;;147113:332:0;-1:-1:-1;;;;;147113:332:0;;;;;;-1:-1:-1;;;;;147113:332:0;-1:-1:-1;;;;;147113:332:0;;;;;;;;;;;;;;;;-1:-1:-1;;;;;147113:332:0;-1:-1:-1;;;;;147113:332:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;147113:332:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;147113:332:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;147113:332:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;147113:332:0;;-1:-1:-1;;146745:712:0;147474:17;:9;-1:-1:-1;;;;;147474:15:0;;:17::i;:::-;147469:655;;147508:21;71874:42;-1:-1:-1;;;;;147532:36:0;;147587:9;147615:34;147668:10;147711:1;147697:16;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;147697:16:0;;147532:196;;;;;;;;;;;;;-1:-1:-1;;;;;147532:196:0;-1:-1:-1;;;;;147532:196:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;147532:196:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;147532:196:0;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;147532:196:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;147532:196:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;147532:196:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;147532:196:0;;;;;;;;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;213:10;;-1:-1;;;244:29;;285:43;;;282:58;-1:-1;233:115;230:2;;;361:1;358;351:12;230:2;372:25;;-1:-1;147532:196:0;;420:4:-1;411:14;;;;147532:196:0;;;;;411:14:-1;147532:196:0;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;147532:196:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;147508:220;;71640:42;-1:-1:-1;;;;;147760:37:0;;147804:12;70089:42;147866:12;147897:9;147933:4;-1:-1:-1;;147987:1:0;148007:42;148068:2;148089:8;147760:352;;;;;;;;;;;;;-1:-1:-1;;;;;147760:352:0;-1:-1:-1;;;;;147760:352:0;;;;;;;;;;;-1:-1:-1;;;;;147760:352:0;-1:-1:-1;;;;;147760:352:0;;;;;;-1:-1:-1;;;;;147760:352:0;-1:-1:-1;;;;;147760:352:0;;;;;;;;;;;;;;;;-1:-1:-1;;;;;147760:352:0;-1:-1:-1;;;;;147760:352:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;147760:352:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;147760:352:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;147760:352:0;;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;77048:951:0;77164:7;77189:17;:9;-1:-1:-1;;;;;77189:15:0;;:17::i;:::-;77188:18;:40;;;;;77211:17;:9;-1:-1:-1;;;;;77211:15:0;;:17::i;:::-;77210:18;77188:40;77184:81;;;-1:-1:-1;77252:1:0;77245:8;;77184:81;77277:27;71751:42;77307:37;77359:17;-1:-1:-1;;;;;77359:15:0;;;:17::i;:::-;:41;;77391:9;77359:41;;;77379:9;77359:41;77307:104;;;;;;;;;;;;;-1:-1:-1;;;;;77307:104:0;-1:-1:-1;;;;;77307:104:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;77307:104:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;77307:104:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;77307:104:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;77307:104:0;;;;;;;;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;219:3;213:10;331:9;325:2;311:12;307:21;289:16;285:44;282:59;-1:-1;;;247:12;244:29;233:116;230:2;;;362:1;359;352:12;230:2;373:25;;-1:-1;77307:104:0;;421:4:-1;412:14;;;;77307:104:0;;;;;412:14:-1;77307:104:0;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;77307:104:0;;;;;;;;;;;77277:134;;77429:6;77438:1;77429:10;;77424:547;77445:10;:17;77441:1;:21;77424:547;;;77515:3;77497:10;77508:1;77497:13;;;;;;;;;;;;;;77489:22;;:29;;77523:4;77488:39;;:153;;;;;77558:10;77569:1;77558:13;;;;;;;;;;;;;;-1:-1:-1;;;;;;77558:83:0;;;;77488:153;:270;;;;;77675:10;77686:1;77675:13;;;;;;;;;;;;;;-1:-1:-1;;;;;;77675:83:0;;;;77488:270;:387;;;;;77792:10;77803:1;77792:13;;;;;;;;;;;;;;-1:-1:-1;;;;;;77792:83:0;;;;77488:387;77484:476;;;77931:10;77942:1;77931:13;;;;;;;;;;;;;;77924:20;;;;;;77484:476;77464:3;;77424:547;;;-1:-1:-1;77990:1:0;;77048:951;-1:-1:-1;;;;77048:951:0:o;13119:136::-;13177:7;13204:43;13208:1;13211;13204:43;;;;;;;;;;;;;;;;;:3;:43::i;35500:204::-;35627:68;;;-1:-1:-1;;;;;35627:68:0;;;;;;;;;;;;;;;;;;;;;;26:21:-1;;;22:32;;;6:49;;35627:68:0;;;;;;;;25:18:-1;;61:17;;-1:-1;;;;;182:15;-1:-1;;;179:29;160:49;;35601:95:0;;35620:5;;35601:18;:95::i;15636:345::-;15722:7;15824:12;15817:5;15809:28;;;;-1:-1:-1;;;15809:28:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;15809:28:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15848:9;15864:1;15860;:5;;;;;;;15636:345;-1:-1:-1;;;;;15636:345:0:o;35316:176::-;35425:58;;;-1:-1:-1;;;;;35425:58:0;;;;;;;;;;;;;;;26:21:-1;;;22:32;;;6:49;;35425:58:0;;;;;;;;25:18:-1;;61:17;;-1:-1;;;;;182:15;-1:-1;;;179:29;160:49;;35399:85:0;;35418:5;;35399:18;:85::i;35712:621::-;36082:10;;;36081:62;;-1:-1:-1;36098:39:0;;;-1:-1:-1;;;36098:39:0;;36122:4;36098:39;;;;-1:-1:-1;;;;;36098:39:0;;;;;;;;;:15;;;;;;:39;;;;;;;;;;;;;;;:15;:39;;;5:2:-1;;;;30:1;27;20:12;5:2;36098:39:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;36098:39:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;36098:39:0;:44;36081:62;36073:152;;;;-1:-1:-1;;;36073:152:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;36262:62;;;-1:-1:-1;;;;;36262:62:0;;;;;;;;;;;;;;;26:21:-1;;;22:32;;;6:49;;36262:62:0;;;;;;;;25:18:-1;;61:17;;-1:-1;;;;;182:15;-1:-1;;;179:29;160:49;;36236:89:0;;36255:5;;36236:18;:89::i;42379:599::-;42544:7;;42584:47;-1:-1:-1;;;;;42584:28:0;;42621:8;42584:47;:28;:47;:::i;:::-;42564:67;-1:-1:-1;42642:18:0;42663:47;-1:-1:-1;;;;;42663:28:0;;42700:8;42663:47;:28;:47;:::i;:::-;42642:68;-1:-1:-1;42723:23:0;42749:17;:8;42762:3;42749:17;:12;:17;:::i;:::-;42723:43;-1:-1:-1;42777:17:0;42797:31;42723:43;42817:10;42797:31;:19;:31;:::i;:::-;42777:51;-1:-1:-1;42839:19:0;42861:40;42885:15;42861:19;:9;42875:4;42861:19;:13;:19;:::i;:::-;:23;:40;:23;:40;:::i;:::-;42839:62;-1:-1:-1;42920:16:0;;42919:51;;42944:26;:9;42958:11;42944:26;:13;:26;:::i;:::-;42919:51;;;42940:1;42919:51;42912:58;42379:599;-1:-1:-1;;;;;;;;;;42379:599:0:o;13592:192::-;13678:7;13714:12;13706:6;;;;13698:29;;;;-1:-1:-1;;;13698:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;13698:29:0;-1:-1:-1;;;13750:5:0;;;13592:192::o;37355:1114::-;37959:27;37967:5;-1:-1:-1;;;;;37959:25:0;;:27::i;:::-;37951:71;;;;;-1:-1:-1;;;37951:71:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;38096:12;38110:23;38145:5;-1:-1:-1;;;;;38137:19:0;38157:4;38137:25;;;;;;;;;;;;;36:153:-1;66:2;61:3;58:11;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;274:1;267:3;263:2;259:12;254:3;250:22;246:30;315:4;311:9;305:3;299:10;295:26;356:4;350:3;344:10;340:21;389:7;380;377:20;372:3;365:33;3:399;;;38137:25:0;;;;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;38095:67:0;;;;38181:7;38173:52;;;;;-1:-1:-1;;;38173:52:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;38242:17;;:21;38238:224;;38384:10;38373:30;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;38373:30:0;38365:85;;;;-1:-1:-1;;;38365:85:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;32345:619;32405:4;32873:20;;32716:66;32913:23;;;;;;:42;;-1:-1:-1;32940:15:0;;;32913:42;32905:51;32345:619;-1:-1:-1;;;;32345:619:0:o;128442:25370::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;
Swarm Source
bzzr://de530c3963242374be6d69dd06ead3f3520b8b1e26334841b7db5b7f12b87679
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 29 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
BSC | 100.00% | $7.95 | 1 | $7.95 |
Loading...
Loading
[ 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.