Contract Overview
My Name Tag:
Not Available, login to update
[ Download CSV Export ]
Latest 1 internal transaction
Parent Txn Hash | Block | From | To | Value | |||
---|---|---|---|---|---|---|---|
0x8010cb1eb67788d11e18c57b7048b8b7b171f72d382b71fff0cd44f11103a691 | 1178328 | 391 days 28 mins ago | 0x05c5db43db72b6e73702eeb1e5b62a03a343732a | Contract Creation | 0 AVAX |
[ Download CSV Export ]
This contract contains unverified libraries: SwapUtils
Contract Name:
SwapFlashLoan
Compiler Version
v0.6.12+commit.27d51765
Contract Source Code (Solidity)
/** *Submitted for verification at snowtrace.io on 2021-11-15 */ // File: contracts/openzeppelin/contracts/utils/math/SafeMath.sol // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File: contracts/openzeppelin/contracts/token/ERC20/IERC20.sol pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File: contracts/openzeppelin/contracts/utils/Address.sol pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // File: contracts/openzeppelin/contracts/token/ERC20/SafeERC20.sol pragma solidity >=0.6.0 <0.8.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 IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File: contracts/openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // File: contracts/openzeppelin/contracts-upgradeable/proxy/Initializable.sol // solhint-disable-next-line compiler-version pragma solidity >=0.4.24 <0.8.0; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function _isConstructor() private view returns (bool) { return !AddressUpgradeable.isContract(address(this)); } } // File: contracts/openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol pragma solidity >=0.6.0 <0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal initializer { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal initializer { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } uint256[49] private __gap; } // File: contracts/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol pragma solidity >=0.6.0 <0.8.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal initializer { __Context_init_unchained(); } function __Context_init_unchained() internal initializer { } function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } uint256[50] private __gap; } // File: contracts/openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol pragma solidity >=0.6.0 <0.8.0; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal initializer { __Context_init_unchained(); __Ownable_init_unchained(); } function __Ownable_init_unchained() internal initializer { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } uint256[49] private __gap; } // File: contracts/openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol pragma solidity >=0.6.0 <0.8.0; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract PausableUpgradeable is Initializable, ContextUpgradeable { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ function __Pausable_init() internal initializer { __Context_init_unchained(); __Pausable_init_unchained(); } function __Pausable_init_unchained() internal initializer { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } uint256[49] private __gap; } // File: contracts/OwnerPausableUpgradeable.sol pragma solidity 0.6.12; /** * @title OwnerPausable * @notice An ownable contract allows the owner to pause and unpause the * contract without a delay. * @dev Only methods using the provided modifiers will be paused. */ abstract contract OwnerPausableUpgradeable is OwnableUpgradeable, PausableUpgradeable { function __OwnerPausable_init() internal initializer { __Context_init_unchained(); __Ownable_init_unchained(); __Pausable_init_unchained(); } /** * @notice Pause the contract. Revert if already paused. */ function pause() external onlyOwner { PausableUpgradeable._pause(); } /** * @notice Unpause the contract. Revert if already unpaused. */ function unpause() external onlyOwner { PausableUpgradeable._unpause(); } } // File: contracts/openzeppelin/contracts/token/ERC20/Context.sol pragma solidity >=0.6.0 <0.8.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File: contracts/openzeppelin/contracts/token/ERC20/ERC20.sol pragma solidity >=0.6.0 <0.8.0; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } // File: contracts/openzeppelin/contracts/token/ERC20/ERC20Burnable.sol pragma solidity >=0.6.0 <0.8.0; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { using SafeMath for uint256; /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, amount); } } // File: contracts/openzeppelin/contracts/access/Ownable.sol pragma solidity >=0.6.0 <0.8.0; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: contracts/interfaces/IAllowlist.sol pragma solidity 0.6.12; interface IAllowlist { function getPoolAccountLimit(address poolAddress) external view returns (uint256); function getPoolCap(address poolAddress) external view returns (uint256); function verifyAddress(address account, bytes32[] calldata merkleProof) external returns (bool); } // File: contracts/interfaces/ISwap.sol pragma solidity 0.6.12; interface ISwap { // pool data view functions function getA() external view returns (uint256); function getAllowlist() external view returns (IAllowlist); function getToken(uint8 index) external view returns (IERC20); function getTokenIndex(address tokenAddress) external view returns (uint8); function getTokenBalance(uint8 index) external view returns (uint256); function getVirtualPrice() external view returns (uint256); function isGuarded() external view returns (bool); // min return calculation functions function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256); function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256); function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory); function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount); // state modifying functions function initialize( IERC20[] memory pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 a, uint256 fee, uint256 adminFee, uint256 withdrawFee ) external; function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external returns (uint256); function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external returns (uint256); function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external returns (uint256[] memory); function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external returns (uint256); function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external returns (uint256); // withdraw fee update function function updateUserWithdrawFee(address recipient, uint256 transferAmount) external; } // File: contracts/LPToken.sol pragma solidity 0.6.12; /** * @title Liquidity Provider Token * @notice This token is an ERC20 detailed token with added capability to be minted by the owner. * It is used to represent user's shares when providing liquidity to swap contracts. */ contract LPToken is ERC20Burnable, Ownable { using SafeMath for uint256; // Address of the swap contract that owns this LP token. When a user adds liquidity to the swap contract, // they receive a proportionate amount of this LPToken. ISwap public swap; /** * @notice Deploys LPToken contract with given name, symbol, and decimals * @dev the caller of this constructor will become the owner of this contract * @param name_ name of this token * @param symbol_ symbol of this token * @param decimals_ number of decimals this token will be based on */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) public ERC20(name_, symbol_) { _setupDecimals(decimals_); swap = ISwap(_msgSender()); } /** * @notice Mints the given amount of LPToken to the recipient. * @dev only owner can call this mint function * @param recipient address of account to receive the tokens * @param amount amount of tokens to mint */ function mint(address recipient, uint256 amount) external onlyOwner { require(amount != 0, "amount == 0"); _mint(recipient, amount); } /** * @dev Overrides ERC20._beforeTokenTransfer() which get called on every transfers including * minting and burning. This ensures that swap.updateUserWithdrawFees are called everytime. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal override(ERC20) { super._beforeTokenTransfer(from, to, amount); swap.updateUserWithdrawFee(to, amount); } } // File: contracts/MathUtils.sol pragma solidity 0.6.12; /** * @title MathUtils library * @notice A library to be used in conjunction with SafeMath. Contains functions for calculating * differences between two uint256. */ library MathUtils { /** * @notice Compares a and b and returns true if the difference between a and b * is less than 1 or equal to each other. * @param a uint256 to compare with * @param b uint256 to compare with * @return True if the difference between a and b is less than 1 or equal, * otherwise return false */ function within1(uint256 a, uint256 b) external pure returns (bool) { return (_difference(a, b) <= 1); } /** * @notice Calculates absolute difference between a and b * @param a uint256 to compare with * @param b uint256 to compare with * @return Difference between a and b */ function difference(uint256 a, uint256 b) external pure returns (uint256) { return _difference(a, b); } /** * @notice Calculates absolute difference between a and b * @param a uint256 to compare with * @param b uint256 to compare with * @return Difference between a and b */ function _difference(uint256 a, uint256 b) internal pure returns (uint256) { if (a > b) { return a - b; } return b - a; } } // File: contracts/SwapUtils.sol pragma solidity 0.6.12; /** * @title SwapUtils library * @notice A library to be used within Swap.sol. Contains functions responsible for custody and AMM functionalities. * @dev Contracts relying on this library must initialize SwapUtils.Swap struct then use this library * for SwapUtils.Swap struct. Note that this library contains both functions called by users and admins. * Admin functions should be protected within contracts using this library. */ library SwapUtils { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; /*** EVENTS ***/ event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); struct Swap { // variables around the ramp management of A, // the amplification coefficient * n * (n - 1) // see https://www.curve.fi/stableswap-paper.pdf for details uint256 initialA; uint256 futureA; uint256 initialATime; uint256 futureATime; // fee calculation uint256 swapFee; uint256 adminFee; uint256 defaultWithdrawFee; LPToken lpToken; // contract references for all tokens being pooled IERC20[] pooledTokens; // multipliers for each pooled token's precision to get to POOL_PRECISION_DECIMALS // for example, TBTC has 18 decimals, so the multiplier should be 1. WBTC // has 8, so the multiplier should be 10 ** 18 / 10 ** 8 => 10 ** 10 uint256[] tokenPrecisionMultipliers; // the pool balance of each token, in the token's precision // the contract's actual token balance might differ uint256[] balances; mapping(address => uint256) depositTimestamp; mapping(address => uint256) withdrawFeeMultiplier; } // Struct storing variables used in calculations in the // calculateWithdrawOneTokenDY function to avoid stack too deep errors struct CalculateWithdrawOneTokenDYInfo { uint256 d0; uint256 d1; uint256 newY; uint256 feePerToken; uint256 preciseA; } // Struct storing variables used in calculation in addLiquidity function // to avoid stack too deep error struct AddLiquidityInfo { uint256 d0; uint256 d1; uint256 d2; uint256 preciseA; } // Struct storing variables used in calculation in removeLiquidityImbalance function // to avoid stack too deep error struct RemoveLiquidityImbalanceInfo { uint256 d0; uint256 d1; uint256 d2; uint256 preciseA; } // the precision all pools tokens will be converted to uint8 public constant POOL_PRECISION_DECIMALS = 18; // the denominator used to calculate admin and LP fees. For example, an // LP fee might be something like tradeAmount.mul(fee).div(FEE_DENOMINATOR) uint256 private constant FEE_DENOMINATOR = 10**10; // Max swap fee is 1% or 100bps of each swap uint256 public constant MAX_SWAP_FEE = 10**8; // Max adminFee is 100% of the swapFee // adminFee does not add additional fee on top of swapFee // Instead it takes a certain % of the swapFee. Therefore it has no impact on the // users but only on the earnings of LPs uint256 public constant MAX_ADMIN_FEE = 10**10; // Max withdrawFee is 1% of the value withdrawn // Fee will be redistributed to the LPs in the pool, rewarding // long term providers. uint256 public constant MAX_WITHDRAW_FEE = 10**8; // Constant value used as max loop limit uint256 private constant MAX_LOOP_LIMIT = 256; // Constant values used in ramping A calculations uint256 public constant A_PRECISION = 100; uint256 public constant MAX_A = 10**6; uint256 private constant MAX_A_CHANGE = 2; uint256 private constant MIN_RAMP_TIME = 14 days; /*** VIEW & PURE FUNCTIONS ***/ /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter */ function getA(Swap storage self) external view returns (uint256) { return _getA(self); } /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter */ function _getA(Swap storage self) internal view returns (uint256) { return _getAPrecise(self).div(A_PRECISION); } /** * @notice Return A in its raw precision * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function getAPrecise(Swap storage self) external view returns (uint256) { return _getAPrecise(self); } /** * @notice Calculates and returns A based on the ramp settings * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function _getAPrecise(Swap storage self) internal view returns (uint256) { uint256 t1 = self.futureATime; // time when ramp is finished uint256 a1 = self.futureA; // final A value when ramp is finished if (block.timestamp < t1) { uint256 t0 = self.initialATime; // time when ramp is started uint256 a0 = self.initialA; // initial A value when ramp is started if (a1 > a0) { // a0 + (a1 - a0) * (block.timestamp - t0) / (t1 - t0) return a0.add( a1.sub(a0).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } else { // a0 - (a0 - a1) * (block.timestamp - t0) / (t1 - t0) return a0.sub( a0.sub(a1).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } } else { return a1; } } /** * @notice Retrieves the timestamp of last deposit made by the given address * @param self Swap struct to read from * @return timestamp of last deposit */ function getDepositTimestamp(Swap storage self, address user) external view returns (uint256) { return self.depositTimestamp[user]; } /** * @notice Calculate the dy, the amount of selected token that user receives and * the fee of withdrawing in one token * @param account the address that is withdrawing * @param tokenAmount the amount to withdraw in the pool's precision * @param tokenIndex which token will be withdrawn * @param self Swap struct to read from * @return the amount of token user will receive and the associated swap fee */ function calculateWithdrawOneToken( Swap storage self, address account, uint256 tokenAmount, uint8 tokenIndex ) public view returns (uint256, uint256) { uint256 dy; uint256 newY; (dy, newY) = calculateWithdrawOneTokenDY(self, tokenIndex, tokenAmount); // dy_0 (without fees) // dy, dy_0 - dy uint256 dySwapFee = _xp(self)[tokenIndex] .sub(newY) .div(self.tokenPrecisionMultipliers[tokenIndex]) .sub(dy); dy = dy .mul( FEE_DENOMINATOR.sub(calculateCurrentWithdrawFee(self, account)) ) .div(FEE_DENOMINATOR); return (dy, dySwapFee); } /** * @notice Calculate the dy of withdrawing in one token * @param self Swap struct to read from * @param tokenIndex which token will be withdrawn * @param tokenAmount the amount to withdraw in the pools precision * @return the d and the new y after withdrawing one token */ function calculateWithdrawOneTokenDY( Swap storage self, uint8 tokenIndex, uint256 tokenAmount ) internal view returns (uint256, uint256) { require( tokenIndex < self.pooledTokens.length, "Token index out of range" ); // Get the current D, then solve the stableswap invariant // y_i for D - tokenAmount uint256[] memory xp = _xp(self); CalculateWithdrawOneTokenDYInfo memory v = CalculateWithdrawOneTokenDYInfo(0, 0, 0, 0, 0); v.preciseA = _getAPrecise(self); v.d0 = getD(xp, v.preciseA); v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(self.lpToken.totalSupply())); require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available"); v.newY = getYD(v.preciseA, tokenIndex, xp, v.d1); uint256[] memory xpReduced = new uint256[](xp.length); v.feePerToken = _feePerToken(self); for (uint256 i = 0; i < self.pooledTokens.length; i++) { uint256 xpi = xp[i]; // if i == tokenIndex, dxExpected = xp[i] * d1 / d0 - newY // else dxExpected = xp[i] - (xp[i] * d1 / d0) // xpReduced[i] -= dxExpected * fee / FEE_DENOMINATOR xpReduced[i] = xpi.sub( ( (i == tokenIndex) ? xpi.mul(v.d1).div(v.d0).sub(v.newY) : xpi.sub(xpi.mul(v.d1).div(v.d0)) ) .mul(v.feePerToken) .div(FEE_DENOMINATOR) ); } uint256 dy = xpReduced[tokenIndex].sub( getYD(v.preciseA, tokenIndex, xpReduced, v.d1) ); dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]); return (dy, v.newY); } /** * @notice Calculate the price of a token in the pool with given * precision-adjusted balances and a particular D. * * @dev This is accomplished via solving the invariant iteratively. * See the StableSwap paper and Curve.fi implementation for further details. * * x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A) * x_1**2 + b*x_1 = c * x_1 = (x_1**2 + c) / (2*x_1 + b) * * @param a the amplification coefficient * n * (n - 1). See the StableSwap paper for details. * @param tokenIndex Index of token we are calculating for. * @param xp a precision-adjusted set of pool balances. Array should be * the same cardinality as the pool. * @param d the stableswap invariant * @return the price of the token, in the same precision as in xp */ function getYD( uint256 a, uint8 tokenIndex, uint256[] memory xp, uint256 d ) internal pure returns (uint256) { uint256 numTokens = xp.length; require(tokenIndex < numTokens, "Token not found"); uint256 c = d; uint256 s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < numTokens; i++) { if (i != tokenIndex) { s = s.add(xp[i]); c = c.mul(d).div(xp[i].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } } c = c.mul(d).mul(A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Get D, the StableSwap invariant, based on a set of balances and a particular A. * @param xp a precision-adjusted set of pool balances. Array should be the same cardinality * as the pool. * @param a the amplification coefficient * n * (n - 1) in A_PRECISION. * See the StableSwap paper for details * @return the invariant, at the precision of the pool */ function getD(uint256[] memory xp, uint256 a) internal pure returns (uint256) { uint256 numTokens = xp.length; uint256 s; for (uint256 i = 0; i < numTokens; i++) { s = s.add(xp[i]); } if (s == 0) { return 0; } uint256 prevD; uint256 d = s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { uint256 dP = d; for (uint256 j = 0; j < numTokens; j++) { dP = dP.mul(d).div(xp[j].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // dP = dP * D * D * D * ... overflow! } prevD = d; d = nA.mul(s).div(A_PRECISION).add(dP.mul(numTokens)).mul(d).div( nA.sub(A_PRECISION).mul(d).div(A_PRECISION).add( numTokens.add(1).mul(dP) ) ); if (d.within1(prevD)) { return d; } } // Convergence should occur in 4 loops or less. If this is reached, there may be something wrong // with the pool. If this were to occur repeatedly, LPs should withdraw via `removeLiquidity()` // function which does not rely on D. revert("D does not converge"); } /** * @notice Get D, the StableSwap invariant, based on self Swap struct * @param self Swap struct to read from * @return The invariant, at the precision of the pool */ function getD(Swap storage self) internal view returns (uint256) { return getD(_xp(self), _getAPrecise(self)); } /** * @notice Given a set of balances and precision multipliers, return the * precision-adjusted balances. * * @param balances an array of token balances, in their native precisions. * These should generally correspond with pooled tokens. * * @param precisionMultipliers an array of multipliers, corresponding to * the amounts in the balances array. When multiplied together they * should yield amounts at the pool's precision. * * @return an array of amounts "scaled" to the pool's precision */ function _xp( uint256[] memory balances, uint256[] memory precisionMultipliers ) internal pure returns (uint256[] memory) { uint256 numTokens = balances.length; require( numTokens == precisionMultipliers.length, "Balances must match multipliers" ); uint256[] memory xp = new uint256[](numTokens); for (uint256 i = 0; i < numTokens; i++) { xp[i] = balances[i].mul(precisionMultipliers[i]); } return xp; } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @param balances array of balances to scale * @return balances array "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(Swap storage self, uint256[] memory balances) internal view returns (uint256[] memory) { return _xp(balances, self.tokenPrecisionMultipliers); } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @return the pool balances "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(Swap storage self) internal view returns (uint256[] memory) { return _xp(self.balances, self.tokenPrecisionMultipliers); } /** * @notice Get the virtual price, to help calculate profit * @param self Swap struct to read from * @return the virtual price, scaled to precision of POOL_PRECISION_DECIMALS */ function getVirtualPrice(Swap storage self) external view returns (uint256) { uint256 d = getD(_xp(self), _getAPrecise(self)); uint256 supply = self.lpToken.totalSupply(); if (supply > 0) { return d.mul(10**uint256(ERC20(self.lpToken).decimals())).div(supply); } return 0; } /** * @notice Calculate the new balances of the tokens given the indexes of the token * that is swapped from (FROM) and the token that is swapped to (TO). * This function is used as a helper function to calculate how much TO token * the user should receive on swap. * * @param self Swap struct to read from * @param tokenIndexFrom index of FROM token * @param tokenIndexTo index of TO token * @param x the new total amount of FROM token * @param xp balances of the tokens in the pool * @return the amount of TO token that should remain in the pool */ function getY( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 x, uint256[] memory xp ) internal view returns (uint256) { uint256 numTokens = self.pooledTokens.length; require( tokenIndexFrom != tokenIndexTo, "Can't compare token to itself" ); require( tokenIndexFrom < numTokens && tokenIndexTo < numTokens, "Tokens must be in pool" ); uint256 a = _getAPrecise(self); uint256 d = getD(xp, a); uint256 c = d; uint256 s; uint256 nA = numTokens.mul(a); uint256 _x; for (uint256 i = 0; i < numTokens; i++) { if (i == tokenIndexFrom) { _x = x; } else if (i != tokenIndexTo) { _x = xp[i]; } else { continue; } s = s.add(_x); c = c.mul(d).div(_x.mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } c = c.mul(d).mul(A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; // iterative approximation for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Externally calculates a swap between two tokens. * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get */ function calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256 dy) { (dy, ) = _calculateSwap(self, tokenIndexFrom, tokenIndexTo, dx); } /** * @notice Internally calculates a swap between two tokens. * * @dev The caller is expected to transfer the actual amounts (dx and dy) * using the token contracts. * * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get * @return dyFee the associated fee */ function _calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) internal view returns (uint256 dy, uint256 dyFee) { uint256[] memory xp = _xp(self); require( tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "Token index out of range" ); uint256 x = dx.mul(self.tokenPrecisionMultipliers[tokenIndexFrom]).add( xp[tokenIndexFrom] ); uint256 y = getY(self, tokenIndexFrom, tokenIndexTo, x, xp); dy = xp[tokenIndexTo].sub(y).sub(1); dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR); dy = dy.sub(dyFee).div(self.tokenPrecisionMultipliers[tokenIndexTo]); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of * LP tokens * * @param account the address that is removing liquidity. required for withdraw fee calculation * @param amount the amount of LP tokens that would to be burned on * withdrawal * @return array of amounts of tokens user will receive */ function calculateRemoveLiquidity( Swap storage self, address account, uint256 amount ) external view returns (uint256[] memory) { return _calculateRemoveLiquidity(self, account, amount); } function _calculateRemoveLiquidity( Swap storage self, address account, uint256 amount ) internal view returns (uint256[] memory) { uint256 totalSupply = self.lpToken.totalSupply(); require(amount <= totalSupply, "Cannot exceed total supply"); uint256 feeAdjustedAmount = amount .mul( FEE_DENOMINATOR.sub(calculateCurrentWithdrawFee(self, account)) ) .div(FEE_DENOMINATOR); uint256[] memory amounts = new uint256[](self.pooledTokens.length); for (uint256 i = 0; i < self.pooledTokens.length; i++) { amounts[i] = self.balances[i].mul(feeAdjustedAmount).div( totalSupply ); } return amounts; } /** * @notice Calculate the fee that is applied when the given user withdraws. * Withdraw fee decays linearly over 4 weeks. * @param user address you want to calculate withdraw fee of * @return current withdraw fee of the user */ function calculateCurrentWithdrawFee(Swap storage self, address user) public view returns (uint256) { uint256 endTime = self.depositTimestamp[user].add(4 weeks); if (endTime > block.timestamp) { uint256 timeLeftover = endTime.sub(block.timestamp); return self .defaultWithdrawFee .mul(self.withdrawFeeMultiplier[user]) .mul(timeLeftover) .div(4 weeks) .div(FEE_DENOMINATOR); } return 0; } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param self Swap struct to read from * @param account address of the account depositing or withdrawing tokens * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return if deposit was true, total amount of lp token that will be minted and if * deposit was false, total amount of lp token that will be burned */ function calculateTokenAmount( Swap storage self, address account, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { uint256 numTokens = self.pooledTokens.length; uint256 a = _getAPrecise(self); uint256 d0 = getD(_xp(self, self.balances), a); uint256[] memory balances1 = self.balances; for (uint256 i = 0; i < numTokens; i++) { if (deposit) { balances1[i] = balances1[i].add(amounts[i]); } else { balances1[i] = balances1[i].sub( amounts[i], "Cannot withdraw more than available" ); } } uint256 d1 = getD(_xp(self, balances1), a); uint256 totalSupply = self.lpToken.totalSupply(); if (deposit) { return d1.sub(d0).mul(totalSupply).div(d0); } else { return d0.sub(d1).mul(totalSupply).div(d0).mul(FEE_DENOMINATOR).div( FEE_DENOMINATOR.sub( calculateCurrentWithdrawFee(self, account) ) ); } } /** * @notice return accumulated amount of admin fees of the token with given index * @param self Swap struct to read from * @param index Index of the pooled token * @return admin balance in the token's precision */ function getAdminBalance(Swap storage self, uint256 index) external view returns (uint256) { require(index < self.pooledTokens.length, "Token index out of range"); return self.pooledTokens[index].balanceOf(address(this)).sub( self.balances[index] ); } /** * @notice internal helper function to calculate fee per token multiplier used in * swap fee calculations * @param self Swap struct to read from */ function _feePerToken(Swap storage self) internal view returns (uint256) { return self.swapFee.mul(self.pooledTokens.length).div( self.pooledTokens.length.sub(1).mul(4) ); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice swap two tokens in the pool * @param self Swap struct to read from and write to * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell * @param minDy the min amount the user would like to receive, or revert. * @return amount of token user received on swap */ function swap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { require( dx <= self.pooledTokens[tokenIndexFrom].balanceOf(msg.sender), "Cannot swap more than you own" ); // Transfer tokens first to see if a fee was charged on transfer uint256 beforeBalance = self.pooledTokens[tokenIndexFrom].balanceOf(address(this)); self.pooledTokens[tokenIndexFrom].safeTransferFrom( msg.sender, address(this), dx ); // Use the actual transferred amount for AMM math uint256 transferredDx = self.pooledTokens[tokenIndexFrom].balanceOf(address(this)).sub( beforeBalance ); (uint256 dy, uint256 dyFee) = _calculateSwap(self, tokenIndexFrom, tokenIndexTo, transferredDx); require(dy >= minDy, "Swap didn't result in min tokens"); uint256 dyAdminFee = dyFee.mul(self.adminFee).div(FEE_DENOMINATOR).div( self.tokenPrecisionMultipliers[tokenIndexTo] ); self.balances[tokenIndexFrom] = self.balances[tokenIndexFrom].add( transferredDx ); self.balances[tokenIndexTo] = self.balances[tokenIndexTo].sub(dy).sub( dyAdminFee ); self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy); emit TokenSwap( msg.sender, transferredDx, dy, tokenIndexFrom, tokenIndexTo ); return dy; } /** * @notice Add liquidity to the pool * @param self Swap struct to read from and write to * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * allowed addresses. If the pool is not in the guarded launch phase, this parameter will be ignored. * @return amount of LP token user received */ function addLiquidity( Swap storage self, uint256[] memory amounts, uint256 minToMint ) external returns (uint256) { require( amounts.length == self.pooledTokens.length, "Amounts must match pooled tokens" ); uint256[] memory fees = new uint256[](self.pooledTokens.length); // current state AddLiquidityInfo memory v = AddLiquidityInfo(0, 0, 0, 0); uint256 totalSupply = self.lpToken.totalSupply(); if (totalSupply != 0) { v.d0 = getD(self); } uint256[] memory newBalances = self.balances; for (uint256 i = 0; i < self.pooledTokens.length; i++) { require( totalSupply != 0 || amounts[i] > 0, "Must supply all tokens in pool" ); // Transfer tokens first to see if a fee was charged on transfer if (amounts[i] != 0) { uint256 beforeBalance = self.pooledTokens[i].balanceOf(address(this)); self.pooledTokens[i].safeTransferFrom( msg.sender, address(this), amounts[i] ); // Update the amounts[] with actual transfer amount amounts[i] = self.pooledTokens[i].balanceOf(address(this)).sub( beforeBalance ); } newBalances[i] = self.balances[i].add(amounts[i]); } // invariant after change v.preciseA = _getAPrecise(self); v.d1 = getD(_xp(self, newBalances), v.preciseA); require(v.d1 > v.d0, "D should increase"); // updated to reflect fees and calculate the user's LP tokens v.d2 = v.d1; if (totalSupply != 0) { uint256 feePerToken = _feePerToken(self); for (uint256 i = 0; i < self.pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(self.balances[i]).div(v.d0); fees[i] = feePerToken .mul(idealBalance.difference(newBalances[i])) .div(FEE_DENOMINATOR); self.balances[i] = newBalances[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); newBalances[i] = newBalances[i].sub(fees[i]); } v.d2 = getD(_xp(self, newBalances), v.preciseA); } else { // the initial depositor doesn't pay fees self.balances = newBalances; } uint256 toMint; if (totalSupply == 0) { toMint = v.d1; } else { toMint = v.d2.sub(v.d0).mul(totalSupply).div(v.d0); } require(toMint >= minToMint, "Couldn't mint min requested"); // mint the user's LP tokens self.lpToken.mint(msg.sender, toMint); emit AddLiquidity( msg.sender, amounts, fees, v.d1, totalSupply.add(toMint) ); return toMint; } /** * @notice Update the withdraw fee for `user`. If the user is currently * not providing liquidity in the pool, sets to default value. If not, recalculate * the starting withdraw fee based on the last deposit's time & amount relative * to the new deposit. * * @param self Swap struct to read from and write to * @param user address of the user depositing tokens * @param toMint amount of pool tokens to be minted */ function updateUserWithdrawFee( Swap storage self, address user, uint256 toMint ) external { _updateUserWithdrawFee(self, user, toMint); } function _updateUserWithdrawFee( Swap storage self, address user, uint256 toMint ) internal { // If token is transferred to address 0 (or burned), don't update the fee. if (user == address(0)) { return; } if (self.defaultWithdrawFee == 0) { // If current fee is set to 0%, set multiplier to FEE_DENOMINATOR self.withdrawFeeMultiplier[user] = FEE_DENOMINATOR; } else { // Otherwise, calculate appropriate discount based on last deposit amount uint256 currentFee = calculateCurrentWithdrawFee(self, user); uint256 currentBalance = self.lpToken.balanceOf(user); // ((currentBalance * currentFee) + (toMint * defaultWithdrawFee)) * FEE_DENOMINATOR / // ((toMint + currentBalance) * defaultWithdrawFee) self.withdrawFeeMultiplier[user] = currentBalance .mul(currentFee) .add(toMint.mul(self.defaultWithdrawFee)) .mul(FEE_DENOMINATOR) .div(toMint.add(currentBalance).mul(self.defaultWithdrawFee)); } self.depositTimestamp[user] = block.timestamp; } /** * @notice Burn LP tokens to remove liquidity from the pool. * @dev Liquidity can always be removed, even when the pool is paused. * @param self Swap struct to read from and write to * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @return amounts of tokens the user received */ function removeLiquidity( Swap storage self, uint256 amount, uint256[] calldata minAmounts ) external returns (uint256[] memory) { require(amount <= self.lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require( minAmounts.length == self.pooledTokens.length, "minAmounts must match poolTokens" ); uint256[] memory amounts = _calculateRemoveLiquidity(self, msg.sender, amount); for (uint256 i = 0; i < amounts.length; i++) { require(amounts[i] >= minAmounts[i], "amounts[i] < minAmounts[i]"); self.balances[i] = self.balances[i].sub(amounts[i]); self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } self.lpToken.burnFrom(msg.sender, amount); emit RemoveLiquidity(msg.sender, amounts, self.lpToken.totalSupply()); return amounts; } /** * @notice Remove liquidity from the pool all in one token. * @param self Swap struct to read from and write to * @param tokenAmount the amount of the lp tokens to burn * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @return amount chosen token that user received */ function removeLiquidityOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount ) external returns (uint256) { uint256 totalSupply = self.lpToken.totalSupply(); uint256 numTokens = self.pooledTokens.length; require( tokenAmount <= self.lpToken.balanceOf(msg.sender), ">LP.balanceOf" ); require(tokenIndex < numTokens, "Token not found"); uint256 dyFee; uint256 dy; (dy, dyFee) = calculateWithdrawOneToken( self, msg.sender, tokenAmount, tokenIndex ); require(dy >= minAmount, "dy < minAmount"); self.balances[tokenIndex] = self.balances[tokenIndex].sub( dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR)) ); self.lpToken.burnFrom(msg.sender, tokenAmount); self.pooledTokens[tokenIndex].safeTransfer(msg.sender, dy); emit RemoveLiquidityOne( msg.sender, tokenAmount, totalSupply, tokenIndex, dy ); return dy; } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. * * @param self Swap struct to read from and write to * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @return actual amount of LP tokens burned in the withdrawal */ function removeLiquidityImbalance( Swap storage self, uint256[] memory amounts, uint256 maxBurnAmount ) public returns (uint256) { require( amounts.length == self.pooledTokens.length, "Amounts should match pool tokens" ); require( maxBurnAmount <= self.lpToken.balanceOf(msg.sender) && maxBurnAmount != 0, ">LP.balanceOf" ); RemoveLiquidityImbalanceInfo memory v = RemoveLiquidityImbalanceInfo(0, 0, 0, 0); uint256 tokenSupply = self.lpToken.totalSupply(); uint256 feePerToken = _feePerToken(self); uint256[] memory balances1 = self.balances; v.preciseA = _getAPrecise(self); v.d0 = getD(_xp(self), v.preciseA); for (uint256 i = 0; i < self.pooledTokens.length; i++) { balances1[i] = balances1[i].sub( amounts[i], "Cannot withdraw more than available" ); } v.d1 = getD(_xp(self, balances1), v.preciseA); uint256[] memory fees = new uint256[](self.pooledTokens.length); for (uint256 i = 0; i < self.pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(self.balances[i]).div(v.d0); uint256 difference = idealBalance.difference(balances1[i]); fees[i] = feePerToken.mul(difference).div(FEE_DENOMINATOR); self.balances[i] = balances1[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); balances1[i] = balances1[i].sub(fees[i]); } v.d2 = getD(_xp(self, balances1), v.preciseA); uint256 tokenAmount = v.d0.sub(v.d2).mul(tokenSupply).div(v.d0); require(tokenAmount != 0, "Burnt amount cannot be zero"); tokenAmount = tokenAmount.add(1).mul(FEE_DENOMINATOR).div( FEE_DENOMINATOR.sub(calculateCurrentWithdrawFee(self, msg.sender)) ); require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount"); self.lpToken.burnFrom(msg.sender, tokenAmount); for (uint256 i = 0; i < self.pooledTokens.length; i++) { self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } emit RemoveLiquidityImbalance( msg.sender, amounts, fees, v.d1, tokenSupply.sub(tokenAmount) ); return tokenAmount; } /** * @notice withdraw all admin fees to a given address * @param self Swap struct to withdraw fees from * @param to Address to send the fees to */ function withdrawAdminFees(Swap storage self, address to) external { for (uint256 i = 0; i < self.pooledTokens.length; i++) { IERC20 token = self.pooledTokens[i]; uint256 balance = token.balanceOf(address(this)).sub(self.balances[i]); if (balance != 0) { token.safeTransfer(to, balance); } } } /** * @notice Sets the admin fee * @dev adminFee cannot be higher than 100% of the swap fee * @param self Swap struct to update * @param newAdminFee new admin fee to be applied on future transactions */ function setAdminFee(Swap storage self, uint256 newAdminFee) external { require(newAdminFee <= MAX_ADMIN_FEE, "Fee is too high"); self.adminFee = newAdminFee; emit NewAdminFee(newAdminFee); } /** * @notice update the swap fee * @dev fee cannot be higher than 1% of each swap * @param self Swap struct to update * @param newSwapFee new swap fee to be applied on future transactions */ function setSwapFee(Swap storage self, uint256 newSwapFee) external { require(newSwapFee <= MAX_SWAP_FEE, "Fee is too high"); self.swapFee = newSwapFee; emit NewSwapFee(newSwapFee); } /** * @notice update the default withdraw fee. This also affects deposits made in the past as well. * @param self Swap struct to update * @param newWithdrawFee new withdraw fee to be applied */ function setDefaultWithdrawFee(Swap storage self, uint256 newWithdrawFee) external { require(newWithdrawFee <= MAX_WITHDRAW_FEE, "Fee is too high"); self.defaultWithdrawFee = newWithdrawFee; emit NewWithdrawFee(newWithdrawFee); } /** * @notice Start ramping up or down A parameter towards given futureA_ and futureTime_ * Checks if the change is too rapid, and commits the new A value only when it falls under * the limit range. * @param self Swap struct to update * @param futureA_ the new A to ramp towards * @param futureTime_ timestamp when the new A should be reached */ function rampA( Swap storage self, uint256 futureA_, uint256 futureTime_ ) external { require( block.timestamp >= self.initialATime.add(1 days), "Wait 1 day before starting ramp" ); require( futureTime_ >= block.timestamp.add(MIN_RAMP_TIME), "Insufficient ramp time" ); require( futureA_ > 0 && futureA_ < MAX_A, "futureA_ must be > 0 and < MAX_A" ); uint256 initialAPrecise = _getAPrecise(self); uint256 futureAPrecise = futureA_.mul(A_PRECISION); if (futureAPrecise < initialAPrecise) { require( futureAPrecise.mul(MAX_A_CHANGE) >= initialAPrecise, "futureA_ is too small" ); } else { require( futureAPrecise <= initialAPrecise.mul(MAX_A_CHANGE), "futureA_ is too large" ); } self.initialA = initialAPrecise; self.futureA = futureAPrecise; self.initialATime = block.timestamp; self.futureATime = futureTime_; emit RampA( initialAPrecise, futureAPrecise, block.timestamp, futureTime_ ); } /** * @notice Stops ramping A immediately. Once this function is called, rampA() * cannot be called for another 24 hours * @param self Swap struct to update */ function stopRampA(Swap storage self) external { require(self.futureATime > block.timestamp, "Ramp is already stopped"); uint256 currentA = _getAPrecise(self); self.initialA = currentA; self.futureA = currentA; self.initialATime = block.timestamp; self.futureATime = block.timestamp; emit StopRampA(currentA, block.timestamp); } } // File: contracts/Swap.sol pragma solidity 0.6.12; /** * @title Swap - A StableSwap implementation in solidity. * @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins) * and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens * in desired ratios for an exchange of the pool token that represents their share of the pool. * Users can burn pool tokens and withdraw their share of token(s). * * Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets * distributed to the LPs. * * In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which * stops the ratio of the tokens in the pool from changing. * Users can always withdraw their tokens via multi-asset withdraws. * * @dev Most of the logic is stored as a library `SwapUtils` for the sake of reducing contract's * deployment size. */ contract Swap is OwnerPausableUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; using SwapUtils for SwapUtils.Swap; // Struct storing data responsible for automatic market maker functionalities. In order to // access this data, this contract uses SwapUtils library. For more details, see SwapUtils.sol SwapUtils.Swap public swapStorage; // True if the contract is initialized. bool private initialized = false; // Maps token address to an index in the pool. Used to prevent duplicate tokens in the pool. // getTokenIndex function also relies on this mapping to retrieve token index. mapping(address => uint8) private tokenIndexes; /*** EVENTS ***/ // events replicated from SwapUtils to make the ABI easier for dumb // clients event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); /** * @notice Initializes this Swap contract with the given parameters. * This will also deploy the LPToken that represents users * LP position. The owner of LPToken will be this contract - which means * only this contract is allowed to mint new tokens. * * @param _pooledTokens an array of ERC20s this pool will accept * @param decimals the decimals to use for each pooled token, * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS * @param lpTokenName the long-form name of the token to be deployed * @param lpTokenSymbol the short symbol for the token to be deployed * @param _a the amplification coefficient * n * (n - 1). See the * StableSwap paper for details * @param _fee default swap fee to be initialized with * @param _adminFee default adminFee to be initialized with * @param _withdrawFee default withdrawFee to be initialized with */ function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, uint256 _withdrawFee ) public virtual initializer { __OwnerPausable_init(); __ReentrancyGuard_init(); // Check _pooledTokens and precisions parameter require(_pooledTokens.length > 1, "_pooledTokens.length <= 1"); require(_pooledTokens.length <= 32, "_pooledTokens.length > 32"); require( _pooledTokens.length == decimals.length, "_pooledTokens decimals mismatch" ); uint256[] memory precisionMultipliers = new uint256[](decimals.length); for (uint8 i = 0; i < _pooledTokens.length; i++) { if (i > 0) { // Check if index is already used. Check if 0th element is a duplicate. require( tokenIndexes[address(_pooledTokens[i])] == 0 && _pooledTokens[0] != _pooledTokens[i], "Duplicate tokens" ); } require( address(_pooledTokens[i]) != address(0), "The 0 address isn't an ERC-20" ); require( decimals[i] <= SwapUtils.POOL_PRECISION_DECIMALS, "Token decimals exceeds max" ); precisionMultipliers[i] = 10 ** uint256(SwapUtils.POOL_PRECISION_DECIMALS).sub( uint256(decimals[i]) ); tokenIndexes[address(_pooledTokens[i])] = i; } // Check _a, _fee, _adminFee, _withdrawFee parameters require(_a < SwapUtils.MAX_A, "_a exceeds maximum"); require(_fee < SwapUtils.MAX_SWAP_FEE, "_fee exceeds maximum"); require( _adminFee < SwapUtils.MAX_ADMIN_FEE, "_adminFee exceeds maximum" ); require( _withdrawFee < SwapUtils.MAX_WITHDRAW_FEE, "_withdrawFee exceeds maximum" ); // Initialize swapStorage struct swapStorage.lpToken = new LPToken( lpTokenName, lpTokenSymbol, SwapUtils.POOL_PRECISION_DECIMALS ); swapStorage.pooledTokens = _pooledTokens; swapStorage.tokenPrecisionMultipliers = precisionMultipliers; swapStorage.balances = new uint256[](_pooledTokens.length); swapStorage.initialA = _a.mul(SwapUtils.A_PRECISION); swapStorage.futureA = _a.mul(SwapUtils.A_PRECISION); swapStorage.initialATime = 0; swapStorage.futureATime = 0; swapStorage.swapFee = _fee; swapStorage.adminFee = _adminFee; swapStorage.defaultWithdrawFee = _withdrawFee; } /*** MODIFIERS ***/ /** * @notice Modifier to check deadline against current timestamp * @param deadline latest timestamp to accept this transaction */ modifier deadlineCheck(uint256 deadline) { require(block.timestamp <= deadline, "Deadline not met"); _; } /*** VIEW FUNCTIONS ***/ /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @return A parameter */ function getA() external view returns (uint256) { return swapStorage.getA(); } /** * @notice Return A in its raw precision form * @dev See the StableSwap paper for details * @return A parameter in its raw precision form */ function getAPrecise() external view returns (uint256) { return swapStorage.getAPrecise(); } /** * @notice Return address of the pooled token at given index. Reverts if tokenIndex is out of range. * @param index the index of the token * @return address of the token at given index */ function getToken(uint8 index) public view returns (IERC20) { require(index < swapStorage.pooledTokens.length, "Out of range"); return swapStorage.pooledTokens[index]; } /** * @notice Return the index of the given token address. Reverts if no matching * token is found. * @param tokenAddress address of the token * @return the index of the given token address */ function getTokenIndex(address tokenAddress) public view returns (uint8) { uint8 index = tokenIndexes[tokenAddress]; require( address(getToken(index)) == tokenAddress, "Token does not exist" ); return index; } /** * @notice Return timestamp of last deposit of given address * @return timestamp of the last deposit made by the given address */ function getDepositTimestamp(address user) external view returns (uint256) { return swapStorage.getDepositTimestamp(user); } /** * @notice Return current balance of the pooled token at given index * @param index the index of the token * @return current balance of the pooled token at given index with token's native precision */ function getTokenBalance(uint8 index) external view returns (uint256) { require(index < swapStorage.pooledTokens.length, "Index out of range"); return swapStorage.balances[index]; } /** * @notice Get the virtual price, to help calculate profit * @return the virtual price, scaled to the POOL_PRECISION_DECIMALS */ function getVirtualPrice() external view returns (uint256) { return swapStorage.getVirtualPrice(); } /** * @notice Calculate amount of tokens you receive on swap * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell. If the token charges * a fee on transfers, use the amount that gets transferred after the fee. * @return amount of tokens the user will receive */ function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256) { return swapStorage.calculateSwap(tokenIndexFrom, tokenIndexTo, dx); } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param account address that is depositing or withdrawing tokens * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return token amount the user will receive */ function calculateTokenAmount( address account, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { return swapStorage.calculateTokenAmount(account, amounts, deposit); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of LP tokens * @param account the address that is withdrawing tokens * @param amount the amount of LP tokens that would be burned on withdrawal * @return array of token balances that the user will receive */ function calculateRemoveLiquidity(address account, uint256 amount) external view returns (uint256[] memory) { return swapStorage.calculateRemoveLiquidity(account, amount); } /** * @notice Calculate the amount of underlying token available to withdraw * when withdrawing via only single token * @param account the address that is withdrawing tokens * @param tokenAmount the amount of LP token to burn * @param tokenIndex index of which token will be withdrawn * @return availableTokenAmount calculated amount of underlying token * available to withdraw */ function calculateRemoveLiquidityOneToken( address account, uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount) { (availableTokenAmount, ) = swapStorage.calculateWithdrawOneToken( account, tokenAmount, tokenIndex ); } /** * @notice Calculate the fee that is applied when the given user withdraws. The withdraw fee * decays linearly over period of 4 weeks. For example, depositing and withdrawing right away * will charge you the full amount of withdraw fee. But withdrawing after 4 weeks will charge you * no additional fees. * @dev returned value should be divided by FEE_DENOMINATOR to convert to correct decimals * @param user address you want to calculate withdraw fee of * @return current withdraw fee of the user */ function calculateCurrentWithdrawFee(address user) external view returns (uint256) { return swapStorage.calculateCurrentWithdrawFee(user); } /** * @notice This function reads the accumulated amount of admin fees of the token with given index * @param index Index of the pooled token * @return admin's token balance in the token's precision */ function getAdminBalance(uint256 index) external view returns (uint256) { return swapStorage.getAdminBalance(index); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice Swap two tokens using this pool * @param tokenIndexFrom the token the user wants to swap from * @param tokenIndexTo the token the user wants to swap to * @param dx the amount of tokens the user wants to swap from * @param minDy the min amount the user would like to receive, or revert. * @param deadline latest timestamp to accept this transaction */ function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.swap(tokenIndexFrom, tokenIndexTo, dx, minDy); } /** * @notice Add liquidity to the pool with the given amounts of tokens * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amount of LP token user minted and received */ function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.addLiquidity(amounts, minToMint); } /** * @notice Burn LP tokens to remove liquidity from the pool. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @dev Liquidity can always be removed, even when the pool is paused. * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amounts of tokens user received */ function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external nonReentrant deadlineCheck(deadline) returns (uint256[] memory) { return swapStorage.removeLiquidity(amount, minAmounts); } /** * @notice Remove liquidity from the pool all in one token. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param tokenAmount the amount of the token you want to receive * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @param deadline latest timestamp to accept this transaction * @return amount of chosen token user received */ function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityOneToken( tokenAmount, tokenIndex, minAmount ); } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @param deadline latest timestamp to accept this transaction * @return amount of LP tokens burned */ function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityImbalance(amounts, maxBurnAmount); } /*** ADMIN FUNCTIONS ***/ /** * @notice Updates the user withdraw fee. This function can only be called by * the pool token. Should be used to update the withdraw fee on transfer of pool tokens. * Transferring your pool token will reset the 4 weeks period. If the recipient is already * holding some pool tokens, the withdraw fee will be discounted in respective amounts. * @param recipient address of the recipient of pool token * @param transferAmount amount of pool token to transfer */ function updateUserWithdrawFee(address recipient, uint256 transferAmount) external { require( msg.sender == address(swapStorage.lpToken), "Only callable by pool token" ); swapStorage.updateUserWithdrawFee(recipient, transferAmount); } /** * @notice Withdraw all admin fees to the contract owner */ function withdrawAdminFees() external onlyOwner { swapStorage.withdrawAdminFees(owner()); } /** * @notice Update the admin fee. Admin fee takes portion of the swap fee. * @param newAdminFee new admin fee to be applied on future transactions */ function setAdminFee(uint256 newAdminFee) external onlyOwner { swapStorage.setAdminFee(newAdminFee); } /** * @notice Update the swap fee to be applied on swaps * @param newSwapFee new swap fee to be applied on future transactions */ function setSwapFee(uint256 newSwapFee) external onlyOwner { swapStorage.setSwapFee(newSwapFee); } /** * @notice Update the withdraw fee. This fee decays linearly over 4 weeks since * user's last deposit. * @param newWithdrawFee new withdraw fee to be applied on future deposits */ function setDefaultWithdrawFee(uint256 newWithdrawFee) external onlyOwner { swapStorage.setDefaultWithdrawFee(newWithdrawFee); } /** * @notice Start ramping up or down A parameter towards given futureA and futureTime * Checks if the change is too rapid, and commits the new A value only when it falls under * the limit range. * @param futureA the new A to ramp towards * @param futureTime timestamp when the new A should be reached */ function rampA(uint256 futureA, uint256 futureTime) external onlyOwner { swapStorage.rampA(futureA, futureTime); } /** * @notice Stop ramping A immediately. Reverts if ramp A is already stopped. */ function stopRampA() external onlyOwner { swapStorage.stopRampA(); } } // File: contracts/interfaces/IFlashLoanReceiver.sol pragma solidity 0.6.12; /** * @title IFlashLoanReceiver interface * @notice Interface for the Saddle fee IFlashLoanReceiver. Modified from Aave's IFlashLoanReceiver interface. * https://github.com/aave/aave-protocol/blob/4b4545fb583fd4f400507b10f3c3114f45b8a037/contracts/flashloan/interfaces/IFlashLoanReceiver.sol * @author Aave * @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract **/ interface IFlashLoanReceiver { function executeOperation( address pool, address token, uint256 amount, uint256 fee, bytes calldata params ) external; } // File: contracts/SwapFlashLoan.sol pragma solidity 0.6.12; /** * @title Swap - A StableSwap implementation in solidity. * @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins) * and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens * in desired ratios for an exchange of the pool token that represents their share of the pool. * Users can burn pool tokens and withdraw their share of token(s). * * Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets * distributed to the LPs. * * In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which * stops the ratio of the tokens in the pool from changing. * Users can always withdraw their tokens via multi-asset withdraws. * * @dev Most of the logic is stored as a library `SwapUtils` for the sake of reducing contract's * deployment size. */ contract SwapFlashLoan is Swap { // Total fee that is charged on all flashloans in BPS. Borrowers must repay the amount plus the flash loan fee. // This fee is split between the protocol and the pool. uint256 public flashLoanFeeBPS; // Share of the flash loan fee that goes to the protocol in BPS. A portion of each flash loan fee is allocated // to the protocol rather than the pool. uint256 public protocolFeeShareBPS; // Max BPS for limiting flash loan fee settings. uint256 public constant MAX_BPS = 10000; /*** EVENTS ***/ event FlashLoan( address indexed receiver, uint8 tokenIndex, uint256 amount, uint256 amountFee, uint256 protocolFee ); /** * @notice Initializes this Swap contract with the given parameters. * This will also deploy the LPToken that represents users * LP position. The owner of LPToken will be this contract - which means * only this contract is allowed to mint new tokens. * * @param _pooledTokens an array of ERC20s this pool will accept * @param decimals the decimals to use for each pooled token, * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS * @param lpTokenName the long-form name of the token to be deployed * @param lpTokenSymbol the short symbol for the token to be deployed * @param _a the amplification coefficient * n * (n - 1). See the * StableSwap paper for details * @param _fee default swap fee to be initialized with * @param _adminFee default adminFee to be initialized with * @param _withdrawFee default withdrawFee to be initialized with */ function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, uint256 _withdrawFee ) public virtual override initializer { Swap.initialize( _pooledTokens, decimals, lpTokenName, lpTokenSymbol, _a, _fee, _adminFee, _withdrawFee ); flashLoanFeeBPS = 100; // 100bps protocolFeeShareBPS = 5000; // 5000bps } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice Borrow the specified token from this pool for this transaction only. This function will call * `IFlashLoanReceiver(receiver).executeOperation` and the `receiver` must return the full amount of the token * and the associated fee by the end of the callback transaction. If the conditions are not met, this call * is reverted. * @param receiver the address of the receiver of the token. This address must implement the IFlashLoanReceiver * interface and the callback function `executeOperation`. * @param token the protocol fee in bps to be applied on the total flash loan fee * @param amount the total amount to borrow in this transaction * @param params optional data to pass along to the callback function */ function flashLoan( address receiver, IERC20 token, uint256 amount, bytes memory params ) external nonReentrant { uint8 tokenIndex = getTokenIndex(address(token)); uint256 availableLiquidityBefore = token.balanceOf(address(this)); uint256 protocolBalanceBefore = availableLiquidityBefore.sub(swapStorage.balances[tokenIndex]); require( amount > 0 && availableLiquidityBefore >= amount, "invalid amount" ); // Calculate the additional amount of tokens the pool should end up with uint256 amountFee = amount.mul(flashLoanFeeBPS).div(10000); // Calculate the portion of the fee that will go to the protocol uint256 protocolFee = amountFee.mul(protocolFeeShareBPS).div(10000); require(amountFee > 0, "amount is small for a flashLoan"); // Transfer the requested amount of tokens token.safeTransfer(receiver, amount); // Execute callback function on receiver IFlashLoanReceiver(receiver).executeOperation( address(this), address(token), amount, amountFee, params ); uint256 availableLiquidityAfter = token.balanceOf(address(this)).sub(protocolBalanceBefore); require( availableLiquidityAfter >= availableLiquidityBefore.add(amountFee), "flashLoan fee is not met" ); swapStorage.balances[tokenIndex] = availableLiquidityAfter.sub( protocolFee ); emit FlashLoan(receiver, tokenIndex, amount, amountFee, protocolFee); } /*** ADMIN FUNCTIONS ***/ /** * @notice Updates the flash loan fee parameters. This function can only be called by the owner. * @param newFlashLoanFeeBPS the total fee in bps to be applied on future flash loans * @param newProtocolFeeShareBPS the protocol fee in bps to be applied on the total flash loan fee */ function setFlashLoanFees( uint256 newFlashLoanFeeBPS, uint256 newProtocolFeeShareBPS ) external onlyOwner { require( newFlashLoanFeeBPS > 0 && newFlashLoanFeeBPS <= MAX_BPS && newProtocolFeeShareBPS <= MAX_BPS, "fees are not in valid range" ); flashLoanFeeBPS = newFlashLoanFeeBPS; protocolFeeShareBPS = newProtocolFeeShareBPS; } }
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint8","name":"tokenIndex","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newAdminFee","type":"uint256"}],"name":"NewAdminFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newSwapFee","type":"uint256"}],"name":"NewSwapFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newWithdrawFee","type":"uint256"}],"name":"NewWithdrawFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"initialTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"futureTime","type":"uint256"}],"name":"RampA","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"RemoveLiquidityImbalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"boughtId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"}],"name":"RemoveLiquidityOne","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"currentA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"StopRampA","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"soldId","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"boughtId","type":"uint128"}],"name":"TokenSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"MAX_BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"minToMint","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"calculateCurrentWithdrawFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculateRemoveLiquidity","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint8","name":"tokenIndex","type":"uint8"}],"name":"calculateRemoveLiquidityOneToken","outputs":[{"internalType":"uint256","name":"availableTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"}],"name":"calculateSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bool","name":"deposit","type":"bool"}],"name":"calculateTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"params","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flashLoanFeeBPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAPrecise","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getAdminBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getDepositTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getTokenIndex","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVirtualPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_pooledTokens","type":"address[]"},{"internalType":"uint8[]","name":"decimals","type":"uint8[]"},{"internalType":"string","name":"lpTokenName","type":"string"},{"internalType":"string","name":"lpTokenSymbol","type":"string"},{"internalType":"uint256","name":"_a","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_adminFee","type":"uint256"},{"internalType":"uint256","name":"_withdrawFee","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeShareBPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"futureA","type":"uint256"},{"internalType":"uint256","name":"futureTime","type":"uint256"}],"name":"rampA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256[]","name":"minAmounts","type":"uint256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"maxBurnAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityImbalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint8","name":"tokenIndex","type":"uint8"},{"internalType":"uint256","name":"minAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityOneToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newAdminFee","type":"uint256"}],"name":"setAdminFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newWithdrawFee","type":"uint256"}],"name":"setDefaultWithdrawFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFlashLoanFeeBPS","type":"uint256"},{"internalType":"uint256","name":"newProtocolFeeShareBPS","type":"uint256"}],"name":"setFlashLoanFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newSwapFee","type":"uint256"}],"name":"setSwapFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stopRampA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"},{"internalType":"uint256","name":"minDy","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapStorage","outputs":[{"internalType":"uint256","name":"initialA","type":"uint256"},{"internalType":"uint256","name":"futureA","type":"uint256"},{"internalType":"uint256","name":"initialATime","type":"uint256"},{"internalType":"uint256","name":"futureATime","type":"uint256"},{"internalType":"uint256","name":"swapFee","type":"uint256"},{"internalType":"uint256","name":"adminFee","type":"uint256"},{"internalType":"uint256","name":"defaultWithdrawFee","type":"uint256"},{"internalType":"contract LPToken","name":"lpToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"transferAmount","type":"uint256"}],"name":"updateUserWithdrawFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAdminFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405260d6805460ff1916905534801561001a57600080fd5b5061545e8061002a6000396000f3fe60806040523480156200001157600080fd5b5060043610620002685760003560e01c806384cdd9bc116200014d578063c4db7fa011620000c9578063ef0a712f1162000087578063ef0a712f1462000af0578063ef8159671462000b10578063f2fde38b1462000b36578063f9273ffb1462000b5f578063fd967f471462000be55762000268565b8063c4db7fa01462000a9f578063d46300fd1462000aa9578063da7a77be1462000ab3578063e25aa5fa1462000adc578063e8cc7fb61462000ae65762000268565b806391ceb3eb116200011757806391ceb3eb14620009c257806398899f4014620009e5578063a95b089f1462000a1d578063ac5bf2631462000a50578063c00c125c1462000a705762000268565b806384cdd9bc14620008df5780638beb60b614620009595780638da5cb5b14620009795780639169558614620009835762000268565b80635c975abb11620001e9578063715018a611620001a7578063715018a614620008535780637c61e561146200085d5780637f1c825a146200088c57806382b8660014620008965780638456cb5914620008d55762000268565b80635c975abb146200048a5780635cffe9de14620004a85780635fd65f0f146200057257806366c0bd2414620005c65780636dd4480b14620006055762000268565b80633e3a156011620002375780633e3a156014620003825780633f4ba83a14620003b75780634a1b0d5714620003c15780634d49e87d14620003ea578063593d132c14620004645762000268565b80630419b45a146200026d5780630ba81959146200027957806331cd52b0146200029557806334e199071462000362575b600080fd5b6200027762000bef565b005b6200028362000cdd565b60408051918252519081900360200190f35b6200031060048036036060811015620002ad57600080fd5b81359190810190604081016020820135600160201b811115620002cf57600080fd5b820183602082011115620002e257600080fd5b803590602001918460208302840111600160201b831117156200030457600080fd5b91935091503562000d65565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156200034e57818101518382015260200162000334565b505050509050019250505060405180910390f35b62000277600480360360208110156200037a57600080fd5b503562000f6e565b62000283600480360360808110156200039a57600080fd5b5080359060ff60208201351690604081013590606001356200104c565b62000277620011dc565b6200028360048036036020811015620003d957600080fd5b50356001600160a01b031662001250565b62000283600480360360608110156200040257600080fd5b810190602081018135600160201b8111156200041d57600080fd5b8201836020820111156200043057600080fd5b803590602001918460208302840111600160201b831117156200045257600080fd5b919350915080359060200135620012ea565b62000277600480360360408110156200047c57600080fd5b508035906020013562001465565b620004946200154b565b604080519115158252519081900360200190f35b6200027760048036036080811015620004c057600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b811115620004fb57600080fd5b8201836020820111156200050e57600080fd5b803590602001918460018302840111600160201b831117156200053057600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955062001554945050505050565b6200057c620019eb565b604080519889526020890197909752878701959095526060870193909352608086019190915260a085015260c08401526001600160a01b031660e083015251908190036101000190f35b620005ef60048036036020811015620005de57600080fd5b50356001600160a01b031662001a0f565b6040805160ff9092168252519081900360200190f35b6200027760048036036101008110156200061e57600080fd5b810190602081018135600160201b8111156200063957600080fd5b8201836020820111156200064c57600080fd5b803590602001918460208302840111600160201b831117156200066e57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115620006be57600080fd5b820183602082011115620006d157600080fd5b803590602001918460208302840111600160201b83111715620006f357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156200074357600080fd5b8201836020820111156200075657600080fd5b803590602001918460018302840111600160201b831117156200077857600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b811115620007cb57600080fd5b820183602082011115620007de57600080fd5b803590602001918460018302840111600160201b831117156200080057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550508235935050506020810135906040810135906060013562001a94565b6200027762001b63565b62000310600480360360408110156200087557600080fd5b506001600160a01b03813516906020013562001c15565b6200028362001d58565b620008b960048036036020811015620008ae57600080fd5b503560ff1662001d5e565b604080516001600160a01b039092168252519081900360200190f35b6200027762001dd7565b6200028360048036036060811015620008f757600080fd5b810190602081018135600160201b8111156200091257600080fd5b8201836020820111156200092557600080fd5b803590602001918460208302840111600160201b831117156200094757600080fd5b91935091508035906020013562001e49565b62000277600480360360208110156200097157600080fd5b503562001fc4565b620008b962002086565b62000283600480360360a08110156200099b57600080fd5b5060ff81358116916020810135909116906040810135906060810135906080013562002095565b6200028360048036036020811015620009da57600080fd5b503560ff166200222e565b6200028360048036036060811015620009fd57600080fd5b5080356001600160a01b0316906020810135906040013560ff16620022a4565b620002836004803603606081101562000a3557600080fd5b5060ff8135811691602081013590911690604001356200234b565b620002776004803603602081101562000a6857600080fd5b5035620023f2565b620002776004803603604081101562000a8857600080fd5b506001600160a01b038135169060200135620024b4565b620002776200257d565b6200028362002638565b620002836004803603602081101562000acb57600080fd5b50356001600160a01b03166200268d565b62000283620026f3565b6200028362002748565b620002836004803603602081101562000b0857600080fd5b50356200274e565b620002776004803603604081101562000b2857600080fd5b5080359060200135620027ab565b620002776004803603602081101562000b4e57600080fd5b50356001600160a01b031662002892565b620002836004803603606081101562000b7757600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111562000ba257600080fd5b82018360208201111562000bb557600080fd5b803590602001918460208302840111600160201b8311171562000bd757600080fd5b91935091503515156200299d565b6200028362002a7b565b62000bf962002a81565b6001600160a01b031662000c0c62002086565b6001600160a01b03161462000c57576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b73cb3828453b688b7ab9ec44fa71653d57344a6eee6324c5c75160c962000c7d62002086565b6040518363ffffffff1660e01b815260040180838152602001826001600160a01b031681526020019250505060006040518083038186803b15801562000cc257600080fd5b505af415801562000cd7573d6000803e3d6000fd5b50505050565b600060c973cb3828453b688b7ab9ec44fa71653d57344a6eee63c9b64dcb90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801562000d3257600080fd5b505af415801562000d47573d6000803e3d6000fd5b505050506040513d602081101562000d5e57600080fd5b5051905090565b60606002609754141562000daf576040805162461bcd60e51b815260206004820152601f602482015260008051602062005324833981519152604482015290519081900360640190fd5b6002609755814281101562000dfe576040805162461bcd60e51b815260206004820152601060248201526f111958591b1a5b99481b9bdd081b595d60821b604482015290519081900360640190fd5b60c973cb3828453b688b7ab9ec44fa71653d57344a6eee6373fd6b3e90918888886040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925060200280828437600081840152601f19601f8201169050808301925050509550505050505060006040518083038186803b15801562000e9057600080fd5b505af415801562000ea5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101562000ecf57600080fd5b8101908080516040519392919084600160201b82111562000eef57600080fd5b90830190602082018581111562000f0557600080fd5b82518660208202830111600160201b8211171562000f2257600080fd5b82525081516020918201928201910280838360005b8381101562000f5157818101518382015260200162000f37565b505050509050016040525050509150506001609755949350505050565b62000f7862002a81565b6001600160a01b031662000f8b62002086565b6001600160a01b03161462000fd6576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b6040805163119f861b60e21b815260c9600482015260248101839052905173cb3828453b688b7ab9ec44fa71653d57344a6eee9163467e186c916044808301926000929190829003018186803b1580156200103057600080fd5b505af415801562001045573d6000803e3d6000fd5b5050505050565b60006002609754141562001096576040805162461bcd60e51b815260206004820152601f602482015260008051602062005324833981519152604482015290519081900360640190fd5b6002609755620010a56200154b565b15620010eb576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b818042111562001135576040805162461bcd60e51b815260206004820152601060248201526f111958591b1a5b99481b9bdd081b595d60821b604482015290519081900360640190fd5b6040805163e7a4db8160e01b815260c960048201526024810188905260ff8716604482015260648101869052905173cb3828453b688b7ab9ec44fa71653d57344a6eee9163e7a4db81916084808301926020929190829003018186803b1580156200119f57600080fd5b505af4158015620011b4573d6000803e3d6000fd5b505050506040513d6020811015620011cb57600080fd5b505160016097559695505050505050565b620011e662002a81565b6001600160a01b0316620011f962002086565b6001600160a01b03161462001244576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b6200124e62002a85565b565b604080516325a3956560e21b815260c960048201526001600160a01b0383166024820152905160009173cb3828453b688b7ab9ec44fa71653d57344a6eee9163968e559491604480820192602092909190829003018186803b158015620012b657600080fd5b505af4158015620012cb573d6000803e3d6000fd5b505050506040513d6020811015620012e257600080fd5b505192915050565b60006002609754141562001334576040805162461bcd60e51b815260206004820152601f602482015260008051602062005324833981519152604482015290519081900360640190fd5b6002609755620013436200154b565b1562001389576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b8180421115620013d3576040805162461bcd60e51b815260206004820152601060248201526f111958591b1a5b99481b9bdd081b595d60821b604482015290519081900360640190fd5b60c973cb3828453b688b7ab9ec44fa71653d57344a6eee6340370edf90918888886040518563ffffffff1660e01b815260040180858152602001806020018381526020018281038252858582818152602001925060200280828437600081840152601f19601f8201169050808301925050509550505050505060206040518083038186803b1580156200119f57600080fd5b6200146f62002a81565b6001600160a01b03166200148262002086565b6001600160a01b031614620014cd576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b604080516358fdd79b60e01b815260c960048201526024810184905260448101839052905173cb3828453b688b7ab9ec44fa71653d57344a6eee916358fdd79b916064808301926000929190829003018186803b1580156200152e57600080fd5b505af415801562001543573d6000803e3d6000fd5b505050505050565b60655460ff1690565b600260975414156200159c576040805162461bcd60e51b815260206004820152601f602482015260008051602062005324833981519152604482015290519081900360640190fd5b60026097556000620015ae8462001a0f565b90506000846001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156200160057600080fd5b505afa15801562001615573d6000803e3d6000fd5b505050506040513d60208110156200162c57600080fd5b505160d3805491925060009162001667919060ff86169081106200164c57fe5b90600052602060002001548362002b2a90919063ffffffff16565b90506000851180156200167a5750848210155b620016bd576040805162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a5908185b5bdd5b9d60921b604482015290519081900360640190fd5b6000620016e4612710620016dd60d8548962002b6e90919063ffffffff16565b9062002bcc565b9050600062001706612710620016dd60d9548562002b6e90919063ffffffff16565b9050600082116200175e576040805162461bcd60e51b815260206004820152601f60248201527f616d6f756e7420697320736d616c6c20666f72206120666c6173684c6f616e00604482015290519081900360640190fd5b620017746001600160a01b0389168a8962002c10565b886001600160a01b031663405b019d308a8a868b6040518663ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b0316815260200184815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101562001800578181015183820152602001620017e6565b50505050905090810190601f1680156200182e5780820380516001836020036101000a031916815260200191505b509650505050505050600060405180830381600087803b1580156200185257600080fd5b505af115801562001867573d6000803e3d6000fd5b505050506000620018f5848a6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015620018c057600080fd5b505afa158015620018d5573d6000803e3d6000fd5b505050506040513d6020811015620018ec57600080fd5b50519062002b2a565b905062001903858462002c69565b81101562001958576040805162461bcd60e51b815260206004820152601860248201527f666c6173684c6f616e20666565206973206e6f74206d65740000000000000000604482015290519081900360640190fd5b62001964818362002b2a565b60d3805460ff89169081106200197657fe5b600091825260209182902001919091556040805160ff891681529182018a905281810185905260608201849052516001600160a01b038c16917f7c186b2827b23e9024e7b29869cba58a97a4bac6567802a8ea6a8afa7b8c22f0919081900360800190a2505060016097555050505050505050565b60c95460ca5460cb5460cc5460cd5460ce5460cf5460d0546001600160a01b031688565b6001600160a01b038116600081815260d76020526040812054909160ff9091169062001a3b8262001d5e565b6001600160a01b03161462001a8e576040805162461bcd60e51b8152602060048201526014602482015273151bdad95b88191bd95cc81b9bdd08195e1a5cdd60621b604482015290519081900360640190fd5b92915050565b600054610100900460ff168062001ab0575062001ab062002cc4565b8062001abf575060005460ff16155b62001afc5760405162461bcd60e51b815260040180806020018281038252602e81526020018062005390602e913960400191505060405180910390fd5b600054610100900460ff1615801562001b28576000805460ff1961ff0019909116610100171660011790555b62001b3a898989898989898962002cd7565b606460d85561138860d955801562001b58576000805461ff00191690555b505050505050505050565b62001b6d62002a81565b6001600160a01b031662001b8062002086565b6001600160a01b03161462001bcb576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380546001600160a01b0319169055565b6040805163f967a81f60e01b815260c960048201526001600160a01b038416602482015260448101839052905160609173cb3828453b688b7ab9ec44fa71653d57344a6eee9163f967a81f91606480820192600092909190829003018186803b15801562001c8257600080fd5b505af415801562001c97573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101562001cc157600080fd5b8101908080516040519392919084600160201b82111562001ce157600080fd5b90830190602082018581111562001cf757600080fd5b82518660208202830111600160201b8211171562001d1457600080fd5b82525081516020918201928201910280838360005b8381101562001d4357818101518382015260200162001d29565b50505050905001604052505050905092915050565b60d85481565b60d15460009060ff83161062001daa576040805162461bcd60e51b815260206004820152600c60248201526b4f7574206f662072616e676560a01b604482015290519081900360640190fd5b60d1805460ff841690811062001dbc57fe5b6000918252602090912001546001600160a01b031692915050565b62001de162002a81565b6001600160a01b031662001df462002086565b6001600160a01b03161462001e3f576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b6200124e620034ac565b60006002609754141562001e93576040805162461bcd60e51b815260206004820152601f602482015260008051602062005324833981519152604482015290519081900360640190fd5b600260975562001ea26200154b565b1562001ee8576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b818042111562001f32576040805162461bcd60e51b815260206004820152601060248201526f111958591b1a5b99481b9bdd081b595d60821b604482015290519081900360640190fd5b60c973cb3828453b688b7ab9ec44fa71653d57344a6eee6341b91c2690918888886040518563ffffffff1660e01b815260040180858152602001806020018381526020018281038252858582818152602001925060200280828437600081840152601f19601f8201169050808301925050509550505050505060206040518083038186803b1580156200119f57600080fd5b62001fce62002a81565b6001600160a01b031662001fe162002086565b6001600160a01b0316146200202c576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b60408051633823393b60e11b815260c9600482015260248101839052905173cb3828453b688b7ab9ec44fa71653d57344a6eee916370467276916044808301926000929190829003018186803b1580156200103057600080fd5b6033546001600160a01b031690565b600060026097541415620020df576040805162461bcd60e51b815260206004820152601f602482015260008051602062005324833981519152604482015290519081900360640190fd5b6002609755620020ee6200154b565b1562002134576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b81804211156200217e576040805162461bcd60e51b815260206004820152601060248201526f111958591b1a5b99481b9bdd081b595d60821b604482015290519081900360640190fd5b6040805163529cbd9160e11b815260c9600482015260ff808a166024830152881660448201526064810187905260848101869052905173cb3828453b688b7ab9ec44fa71653d57344a6eee9163a5397b229160a4808301926020929190829003018186803b158015620021f057600080fd5b505af415801562002205573d6000803e3d6000fd5b505050506040513d60208110156200221c57600080fd5b50516001609755979650505050505050565b60d15460009060ff83161062002280576040805162461bcd60e51b8152602060048201526012602482015271496e646578206f7574206f662072616e676560701b604482015290519081900360640190fd5b60d3805460ff84169081106200229257fe5b90600052602060002001549050919050565b60408051637254d06d60e11b815260c960048201526001600160a01b03851660248201526044810184905260ff83166064820152815160009273cb3828453b688b7ab9ec44fa71653d57344a6eee9263e4a9a0da9260848083019392829003018186803b1580156200231557600080fd5b505af41580156200232a573d6000803e3d6000fd5b505050506040513d60408110156200234157600080fd5b5051949350505050565b604080516312c8d80f60e21b815260c9600482015260ff80861660248301528416604482015260648101839052905160009173cb3828453b688b7ab9ec44fa71653d57344a6eee91634b23603c91608480820192602092909190829003018186803b158015620023ba57600080fd5b505af4158015620023cf573d6000803e3d6000fd5b505050506040513d6020811015620023e657600080fd5b505190505b9392505050565b620023fc62002a81565b6001600160a01b03166200240f62002086565b6001600160a01b0316146200245a576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b6040805163455bb4bf60e11b815260c9600482015260248101839052905173cb3828453b688b7ab9ec44fa71653d57344a6eee91638ab7697e916044808301926000929190829003018186803b1580156200103057600080fd5b60d0546001600160a01b0316331462002514576040805162461bcd60e51b815260206004820152601b60248201527f4f6e6c792063616c6c61626c6520627920706f6f6c20746f6b656e0000000000604482015290519081900360640190fd5b6040805163749296a360e11b815260c960048201526001600160a01b038416602482015260448101839052905173cb3828453b688b7ab9ec44fa71653d57344a6eee9163e9252d46916064808301926000929190829003018186803b1580156200152e57600080fd5b6200258762002a81565b6001600160a01b03166200259a62002086565b6001600160a01b031614620025e5576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b604080516378a7108f60e11b815260c96004820152905173cb3828453b688b7ab9ec44fa71653d57344a6eee9163f14e211e916024808301926000929190829003018186803b15801562000cc257600080fd5b600060c973cb3828453b688b7ab9ec44fa71653d57344a6eee63b0a14cfc90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801562000d3257600080fd5b60408051633a311e9560e21b815260c960048201526001600160a01b0383166024820152905160009173cb3828453b688b7ab9ec44fa71653d57344a6eee9163e8c47a5491604480820192602092909190829003018186803b158015620012b657600080fd5b600060c973cb3828453b688b7ab9ec44fa71653d57344a6eee6371906c2c90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801562000d3257600080fd5b60d95481565b600060c973cb3828453b688b7ab9ec44fa71653d57344a6eee637d0481609091846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b158015620012b657600080fd5b620027b562002a81565b6001600160a01b0316620027c862002086565b6001600160a01b03161462002813576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b6000821180156200282657506127108211155b80156200283557506127108111155b62002887576040805162461bcd60e51b815260206004820152601b60248201527f6665657320617265206e6f7420696e2076616c69642072616e67650000000000604482015290519081900360640190fd5b60d89190915560d955565b6200289c62002a81565b6001600160a01b0316620028af62002086565b6001600160a01b031614620028fa576040805162461bcd60e51b81526020600482018190526024820152600080516020620053df833981519152604482015290519081900360640190fd5b6001600160a01b038116620029415760405162461bcd60e51b8152600401808060200182810382526026815260200180620053446026913960400191505060405180910390fd5b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380546001600160a01b0319166001600160a01b0392909216919091179055565b600060c973cb3828453b688b7ab9ec44fa71653d57344a6eee635e58579c9091878787876040518663ffffffff1660e01b815260040180868152602001856001600160a01b031681526020018060200183151581526020018281038252858582818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060206040518083038186803b15801562002a4457600080fd5b505af415801562002a59573d6000803e3d6000fd5b505050506040513d602081101562002a7057600080fd5b505195945050505050565b61271081565b3390565b62002a8f6200154b565b62002ad8576040805162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015290519081900360640190fd5b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa62002b0d62002a81565b604080516001600160a01b039092168252519081900360200190a1565b6000620023eb83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525062003534565b60008262002b7f5750600062001a8e565b8282028284828162002b8d57fe5b0414620023eb5760405162461bcd60e51b8152600401808060200182810382526021815260200180620053be6021913960400191505060405180910390fd5b6000620023eb83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250620035cf565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905262002c6490849062003638565b505050565b600082820183811015620023eb576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600062002cd130620036ef565b15905090565b600054610100900460ff168062002cf3575062002cf362002cc4565b8062002d02575060005460ff16155b62002d3f5760405162461bcd60e51b815260040180806020018281038252602e81526020018062005390602e913960400191505060405180910390fd5b600054610100900460ff1615801562002d6b576000805460ff1961ff0019909116610100171660011790555b62002d75620036f5565b62002d7f620037bd565b600189511162002dd6576040805162461bcd60e51b815260206004820152601960248201527f5f706f6f6c6564546f6b656e732e6c656e677468203c3d203100000000000000604482015290519081900360640190fd5b60208951111562002e2e576040805162461bcd60e51b815260206004820152601960248201527f5f706f6f6c6564546f6b656e732e6c656e677468203e20333200000000000000604482015290519081900360640190fd5b875189511462002e85576040805162461bcd60e51b815260206004820152601f60248201527f5f706f6f6c6564546f6b656e7320646563696d616c73206d69736d6174636800604482015290519081900360640190fd5b6060885167ffffffffffffffff8111801562002ea057600080fd5b5060405190808252806020026020018201604052801562002ecb578160200160208202803683370190505b50905060005b8a518160ff1610156200314d5760ff81161562002fb45760d760008c8360ff168151811062002efc57fe5b6020908102919091018101516001600160a01b031682528101919091526040016000205460ff1615801562002f6f57508a8160ff168151811062002f3c57fe5b60200260200101516001600160a01b03168b60008151811062002f5b57fe5b60200260200101516001600160a01b031614155b62002fb4576040805162461bcd60e51b815260206004820152601060248201526f4475706c696361746520746f6b656e7360801b604482015290519081900360640190fd5b60006001600160a01b03168b8260ff168151811062002fcf57fe5b60200260200101516001600160a01b0316141562003034576040805162461bcd60e51b815260206004820152601d60248201527f546865203020616464726573732069736e277420616e204552432d3230000000604482015290519081900360640190fd5b601260ff168a8260ff16815181106200304957fe5b602002602001015160ff161115620030a8576040805162461bcd60e51b815260206004820152601a60248201527f546f6b656e20646563696d616c732065786365656473206d6178000000000000604482015290519081900360640190fd5b620030db8a8260ff1681518110620030bc57fe5b602002602001015160ff16601260ff1662002b2a90919063ffffffff16565b600a0a828260ff1681518110620030ee57fe5b6020026020010181815250508060d760008d8460ff16815181106200310f57fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191660ff9290921691909117905560010162002ed1565b50620f424086106200319b576040805162461bcd60e51b81526020600482015260126024820152715f612065786365656473206d6178696d756d60701b604482015290519081900360640190fd5b6305f5e1008510620031eb576040805162461bcd60e51b81526020600482015260146024820152735f6665652065786365656473206d6178696d756d60601b604482015290519081900360640190fd5b6402540be400841062003245576040805162461bcd60e51b815260206004820152601960248201527f5f61646d696e4665652065786365656473206d6178696d756d00000000000000604482015290519081900360640190fd5b6305f5e10083106200329e576040805162461bcd60e51b815260206004820152601c60248201527f5f77697468647261774665652065786365656473206d6178696d756d00000000604482015290519081900360640190fd5b87876012604051620032b09062003d52565b60ff82166040820152606080825284519082015283518190602080830191608084019188019080838360005b83811015620032f6578181015183820152602001620032dc565b50505050905090810190601f168015620033245780820380516001836020036101000a031916815260200191505b50838103825285518152855160209182019187019080838360005b83811015620033595781810151838201526020016200333f565b50505050905090810190601f168015620033875780820380516001836020036101000a031916815260200191505b5095505050505050604051809103906000f080158015620033ac573d6000803e3d6000fd5b5060d080546001600160a01b0319166001600160a01b03929092169190911790558951620033e29060d19060208d019062003d60565b508051620033f89060d290602084019062003dca565b50895167ffffffffffffffff811180156200341257600080fd5b506040519080825280602002602001820160405280156200343d578160200160208202803683370190505b508051620034549160d39160209091019062003dca565b506200346286606462002b6e565b60c9556200347286606462002b6e565b60ca5550600060cb81905560cc5560cd84905560ce83905560cf829055801562001b58576000805461ff0019169055505050505050505050565b620034b66200154b565b15620034fc576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25862002b0d62002a81565b60008184841115620035c75760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156200358b57818101518382015260200162003571565b50505050905090810190601f168015620035b95780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b60008183620036215760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156200358b57818101518382015260200162003571565b5060008385816200362e57fe5b0495945050505050565b60606200368f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166200385b9092919063ffffffff16565b80519091501562002c6457808060200190516020811015620036b057600080fd5b505162002c645760405162461bcd60e51b815260040180806020018281038252602a815260200180620053ff602a913960400191505060405180910390fd5b3b151590565b600054610100900460ff16806200371157506200371162002cc4565b8062003720575060005460ff16155b6200375d5760405162461bcd60e51b815260040180806020018281038252602e81526020018062005390602e913960400191505060405180910390fd5b600054610100900460ff1615801562003789576000805460ff1961ff0019909116610100171660011790555b6200379362003874565b6200379d6200391c565b620037a762003a1f565b8015620037ba576000805461ff00191690555b50565b600054610100900460ff1680620037d95750620037d962002cc4565b80620037e8575060005460ff16155b620038255760405162461bcd60e51b815260040180806020018281038252602e81526020018062005390602e913960400191505060405180910390fd5b600054610100900460ff1615801562003851576000805460ff1961ff0019909116610100171660011790555b620037a762003ad2565b60606200386c848460008562003b80565b949350505050565b600054610100900460ff16806200389057506200389062002cc4565b806200389f575060005460ff16155b620038dc5760405162461bcd60e51b815260040180806020018281038252602e81526020018062005390602e913960400191505060405180910390fd5b600054610100900460ff16158015620037a7576000805460ff1961ff0019909116610100171660011790558015620037ba576000805461ff001916905550565b600054610100900460ff16806200393857506200393862002cc4565b8062003947575060005460ff16155b620039845760405162461bcd60e51b815260040180806020018281038252602e81526020018062005390602e913960400191505060405180910390fd5b600054610100900460ff16158015620039b0576000805460ff1961ff0019909116610100171660011790555b6000620039bc62002a81565b603380546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3508015620037ba576000805461ff001916905550565b600054610100900460ff168062003a3b575062003a3b62002cc4565b8062003a4a575060005460ff16155b62003a875760405162461bcd60e51b815260040180806020018281038252602e81526020018062005390602e913960400191505060405180910390fd5b600054610100900460ff1615801562003ab3576000805460ff1961ff0019909116610100171660011790555b6065805460ff191690558015620037ba576000805461ff001916905550565b600054610100900460ff168062003aee575062003aee62002cc4565b8062003afd575060005460ff16155b62003b3a5760405162461bcd60e51b815260040180806020018281038252602e81526020018062005390602e913960400191505060405180910390fd5b600054610100900460ff1615801562003b66576000805460ff1961ff0019909116610100171660011790555b60016097558015620037ba576000805461ff001916905550565b60608247101562003bc35760405162461bcd60e51b81526004018080602001828103825260268152602001806200536a6026913960400191505060405180910390fd5b62003bce85620036ef565b62003c20576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b6020831062003c615780518252601f19909201916020918201910162003c40565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d806000811462003cc5576040519150601f19603f3d011682016040523d82523d6000602084013e62003cca565b606091505b509150915062003cdc82828662003ce7565b979650505050505050565b6060831562003cf8575081620023eb565b82511562003d095782518084602001fd5b60405162461bcd60e51b81526020600482018181528451602484015284518593919283926044019190850190808383600083156200358b57818101518382015260200162003571565b6114d58062003e4f83390190565b82805482825590600052602060002090810192821562003db8579160200282015b8281111562003db857825182546001600160a01b0319166001600160a01b0390911617825560209092019160019091019062003d81565b5062003dc692915062003e16565b5090565b82805482825590600052602060002090810192821562003e08579160200282015b8281111562003e0857825182559160200191906001019062003deb565b5062003dc692915062003e37565b5b8082111562003dc65780546001600160a01b031916815560010162003e17565b5b8082111562003dc6576000815560010162003e3856fe60806040523480156200001157600080fd5b50604051620014d5380380620014d5833981810160405260608110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b9083019060208201858111156200006e57600080fd5b82516401000000008111828201881017156200008957600080fd5b82525081516020918201929091019080838360005b83811015620000b85781810151838201526020016200009e565b50505050905090810190601f168015620000e65780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200010a57600080fd5b9083019060208201858111156200012057600080fd5b82516401000000008111828201881017156200013b57600080fd5b82525081516020918201929091019080838360005b838110156200016a57818101518382015260200162000150565b50505050905090810190601f168015620001985780820380516001836020036101000a031916815260200191505b5060405260209081015185519093508592508491620001bd916003918501906200029e565b508051620001d39060049060208401906200029e565b50506005805460ff19166012179055506000620001ef62000284565b60058054610100600160a81b0319166101006001600160a01b03841690810291909117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350620002508162000288565b6200025a62000284565b600680546001600160a01b0319166001600160a01b0392909216919091179055506200033a915050565b3390565b6005805460ff191660ff92909216919091179055565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620002e157805160ff191683800117855562000311565b8280016001018555821562000311579182015b8281111562000311578251825591602001919060010190620002f4565b506200031f92915062000323565b5090565b5b808211156200031f576000815560010162000324565b61118b806200034a6000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c8063715018a6116100a257806395d89b411161007157806395d89b4114610343578063a457c2d71461034b578063a9059cbb14610377578063dd62ed3e146103a3578063f2fde38b146103d157610116565b8063715018a6146102e357806379cc6790146102eb5780638119c065146103175780638da5cb5b1461033b57610116565b8063313ce567116100e9578063313ce56714610228578063395093511461024657806340c10f191461027257806342966c68146102a057806370a08231146102bd57610116565b806306fdde031461011b578063095ea7b31461019857806318160ddd146101d857806323b872dd146101f2575b600080fd5b6101236103f7565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561015d578181015183820152602001610145565b50505050905090810190601f16801561018a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101c4600480360360408110156101ae57600080fd5b506001600160a01b03813516906020013561048d565b604080519115158252519081900360200190f35b6101e06104aa565b60408051918252519081900360200190f35b6101c46004803603606081101561020857600080fd5b506001600160a01b038135811691602081013590911690604001356104b0565b610230610537565b6040805160ff9092168252519081900360200190f35b6101c46004803603604081101561025c57600080fd5b506001600160a01b038135169060200135610540565b61029e6004803603604081101561028857600080fd5b506001600160a01b03813516906020013561058e565b005b61029e600480360360208110156102b657600080fd5b503561064b565b6101e0600480360360208110156102d357600080fd5b50356001600160a01b031661065f565b61029e61067a565b61029e6004803603604081101561030157600080fd5b506001600160a01b038135169060200135610739565b61031f610793565b604080516001600160a01b039092168252519081900360200190f35b61031f6107a2565b6101236107b6565b6101c46004803603604081101561036157600080fd5b506001600160a01b038135169060200135610817565b6101c46004803603604081101561038d57600080fd5b506001600160a01b03813516906020013561087f565b6101e0600480360360408110156103b957600080fd5b506001600160a01b0381358116916020013516610893565b61029e600480360360208110156103e757600080fd5b50356001600160a01b03166108be565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104835780601f1061045857610100808354040283529160200191610483565b820191906000526020600020905b81548152906001019060200180831161046657829003601f168201915b5050505050905090565b60006104a161049a6109d9565b84846109dd565b50600192915050565b60025490565b60006104bd848484610ac9565b61052d846104c96109d9565b6105288560405180606001604052806028815260200161107b602891396001600160a01b038a166000908152600160205260408120906105076109d9565b6001600160a01b031681526020810191909152604001600020549190610c24565b6109dd565b5060019392505050565b60055460ff1690565b60006104a161054d6109d9565b84610528856001600061055e6109d9565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490610cbb565b6105966109d9565b60055461010090046001600160a01b039081169116146105fd576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b8061063d576040805162461bcd60e51b815260206004820152600b60248201526a0616d6f756e74203d3d20360ac1b604482015290519081900360640190fd5b6106478282610d1c565b5050565b61065c6106566109d9565b82610e0c565b50565b6001600160a01b031660009081526020819052604090205490565b6106826109d9565b60055461010090046001600160a01b039081169116146106e9576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b60055460405160009161010090046001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a360058054610100600160a81b0319169055565b6000610770826040518060600160405280602481526020016110a360249139610769866107646109d9565b610893565b9190610c24565b90506107848361077e6109d9565b836109dd565b61078e8383610e0c565b505050565b6006546001600160a01b031681565b60055461010090046001600160a01b031690565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104835780601f1061045857610100808354040283529160200191610483565b60006104a16108246109d9565b8461052885604051806060016040528060258152602001611131602591396001600061084e6109d9565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190610c24565b60006104a161088c6109d9565b8484610ac9565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6108c66109d9565b60055461010090046001600160a01b0390811691161461092d576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166109725760405162461bcd60e51b815260040180806020018281038252602681526020018061100d6026913960400191505060405180910390fd5b6005546040516001600160a01b0380841692610100900416907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600580546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b3390565b6001600160a01b038316610a225760405162461bcd60e51b815260040180806020018281038252602481526020018061110d6024913960400191505060405180910390fd5b6001600160a01b038216610a675760405162461bcd60e51b81526004018080602001828103825260228152602001806110336022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b038316610b0e5760405162461bcd60e51b81526004018080602001828103825260258152602001806110e86025913960400191505060405180910390fd5b6001600160a01b038216610b535760405162461bcd60e51b8152600401808060200182810382526023815260200180610fc86023913960400191505060405180910390fd5b610b5e838383610f08565b610b9b81604051806060016040528060268152602001611055602691396001600160a01b0386166000908152602081905260409020549190610c24565b6001600160a01b038085166000908152602081905260408082209390935590841681522054610bca9082610cbb565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60008184841115610cb35760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610c78578181015183820152602001610c60565b50505050905090810190601f168015610ca55780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600082820183811015610d15576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6001600160a01b038216610d77576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b610d8360008383610f08565b600254610d909082610cbb565b6002556001600160a01b038216600090815260208190526040902054610db69082610cbb565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6001600160a01b038216610e515760405162461bcd60e51b81526004018080602001828103825260218152602001806110c76021913960400191505060405180910390fd5b610e5d82600083610f08565b610e9a81604051806060016040528060228152602001610feb602291396001600160a01b0385166000908152602081905260409020549190610c24565b6001600160a01b038316600090815260208190526040902055600254610ec09082610f85565b6002556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b610f1383838361078e565b60065460408051633003049760e21b81526001600160a01b038581166004830152602482018590529151919092169163c00c125c91604480830192600092919082900301818387803b158015610f6857600080fd5b505af1158015610f7c573d6000803e3d6000fd5b50505050505050565b6000610d1583836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250610c2456fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e63654f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a206275726e20616d6f756e74206578636565647320616c6c6f77616e636545524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa26469706673582212206defd27da0955d842feeb80244f327a6e13b8960c046de71dbd8234d4315a88964736f6c634300060c00335265656e7472616e637947756172643a207265656e7472616e742063616c6c004f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65725361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220bb1f3710ea2222062f6ceb513fc425261868ab3f070035c57887d01b3bbf113564736f6c634300060c0033
Deployed ByteCode Sourcemap
130134:5720:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;126686:105;;;:::i;:::-;;115104:106;;;:::i;:::-;;;;;;;;;;;;;;;;123599:270;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;123599:270:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;123599:270:0;;;;;;;;;;;;-1:-1:-1;123599:270:0;-1:-1:-1;123599:270:0;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;127251:112;;;;;;;;;;;;;;;;-1:-1:-1;127251:112:0;;:::i;124391:456::-;;;;;;;;;;;;;;;;-1:-1:-1;124391:456:0;;;;;;;;;;;;;;;;;;;:::i;38446:87::-;;;:::i;120847:186::-;;;;;;;;;;;;;;;;-1:-1:-1;120847:186:0;-1:-1:-1;;;;;120847:186:0;;:::i;122696:319::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;122696:319:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;122696:319:0;;;;;;;;;;;;-1:-1:-1;122696:319:0;-1:-1:-1;122696:319:0;;;;;;;:::i;128080:128::-;;;;;;;;;;;;;;;;-1:-1:-1;128080:128:0;;;;;;;:::i;36396:86::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;133329:1712;;;;;;;;;;;;;;;;-1:-1:-1;;;;;133329:1712:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;133329:1712:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;133329:1712:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;133329:1712:0;;-1:-1:-1;133329:1712:0;;-1:-1:-1;;;;;133329:1712:0:i;108703:33::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;108703:33:0;;;;;;;;;;;;;;115866:276;;;;;;;;;;;;;;;;-1:-1:-1;115866:276:0;-1:-1:-1;;;;;115866:276:0;;:::i;:::-;;;;;;;;;;;;;;;;;;;131849:641;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;131849:641:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;131849:641:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;131849:641:0;;;;;;;;-1:-1:-1;131849:641:0;;-1:-1:-1;;;;;131849:641:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;131849:641:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;131849:641:0;;;;;;;;-1:-1:-1;131849:641:0;;-1:-1:-1;;;;;131849:641:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;131849:641:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;131849:641:0;;;;;;;;-1:-1:-1;131849:641:0;;-1:-1:-1;;;;;131849:641:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;131849:641:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;131849:641:0;;-1:-1:-1;;131849:641:0;;;-1:-1:-1;;;131849:641:0;;;;;;;;;;;;;;:::i;34477:148::-;;;:::i;119269:219::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;119269:219:0;;;;;;;;:::i;130350:30::-;;;:::i;115438:192::-;;;;;;;;;;;;;;;;-1:-1:-1;115438:192:0;;;;:::i;:::-;;;;-1:-1:-1;;;;;115438:192:0;;;;;;;;;;;;;;38271:83;;;:::i;125389:351::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;125389:351:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;125389:351:0;;;;;;;;;;;;-1:-1:-1;125389:351:0;-1:-1:-1;125389:351:0;;;;;;;:::i;126974:116::-;;;;;;;;;;;;;;;;-1:-1:-1;126974:116:0;;:::i;33826:87::-;;;:::i;121867:364::-;;;;;;;;;;;;;;;;-1:-1:-1;121867:364:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;116685:204::-;;;;;;;;;;;;;;;;-1:-1:-1;116685:204:0;;;;:::i;119930:352::-;;;;;;;;;;;;;;;;-1:-1:-1;119930:352:0;;-1:-1:-1;;;;;119930:352:0;;;;;;;;;;;;;:::i;117597:228::-;;;;;;;;;;;;;;;;-1:-1:-1;117597:228:0;;;;;;;;;;;;;;;;;;:::i;127583:142::-;;;;;;;;;;;;;;;;-1:-1:-1;127583:142:0;;:::i;126292:306::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;126292:306:0;;;;;;;;:::i;128316:82::-;;;:::i;114831:92::-;;;:::i;116306:138::-;;;;;;;;;;;;;;;;-1:-1:-1;116306:138:0;-1:-1:-1;;;;;116306:138:0;;:::i;117052:114::-;;;:::i;130549:34::-;;;:::i;121272:132::-;;;;;;;;;;;;;;;;-1:-1:-1;121272:132:0;;:::i;135397:454::-;;;;;;;;;;;;;;;;-1:-1:-1;135397:454:0;;;;;;;:::i;34780:244::-;;;;;;;;;;;;;;;;-1:-1:-1;34780:244:0;-1:-1:-1;;;;;34780:244:0;;:::i;118651:240::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;118651:240:0;;;;;;;;;;;;;;;-1:-1:-1;;;118651:240:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;118651:240:0;;;;;;;;;;;;-1:-1:-1;118651:240:0;-1:-1:-1;118651:240:0;;;;:::i;130644:39::-;;;:::i;126686:105::-;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;126745:29:::1;;:11;126775:7;:5;:7::i;:::-;126745:38;;;;;;;;;;;;;;;;;;-1:-1:-1::0;;;;;126745:38:0::1;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;126686:105::o:0;115104:106::-;115150:7;115177:11;:23;;;;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;115177:25:0;;-1:-1:-1;115104:106:0;:::o;123599:270::-;123778:16;30073:1;30836:7;;:19;;30828:63;;;;;-1:-1:-1;;;30828:63:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;30828:63:0;;;;;;;;;;;;;;;30073:1;30969:7;:18;123759:8;114557:15:::1;:27:::0;-1:-1:-1;114557:27:0::1;114549:56;;;::::0;;-1:-1:-1;;;114549:56:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;114549:56:0;;;;;;;;;;;;;::::1;;123814:11:::2;:27;;;;123842:6;123850:10;;123814:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;::::0;;::::2;-1:-1:-1::0;;123814:47:0::2;::::0;::::2;;::::0;::::2;::::0;::::2;;;;;::::0;::::2;;;;;;;;;;;;;;;-1:-1:-1::0;;;123814:47:0::2;;;;;;::::0;::::2;;::::0;;::::2;::::0;::::2;::::0;::::2;::::0;;::::2;;;;;::::0;::::2;;;;;;;;;;;-1:-1:-1::0;;;123814:47:0::2;;;;;;;::::0;::::2;;::::0;;-1:-1:-1;123814:47:0;;::::2;::::0;;::::2;::::0;;::::2;::::0;::::2;::::0;;;::::2;;;;;;;;::::0;;::::2;::::0;;;::::2;::::0;::::2;;;;;;;;;;;;;;::::0;::::2;;123807:54;;-1:-1:-1::0;30029:1:0;31148:7;:22;123599:270;;-1:-1:-1;;;;123599:270:0:o;127251:112::-;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;127321:34:::1;::::0;;-1:-1:-1;;;127321:34:0;;:11:::1;:34;::::0;::::1;::::0;;;;;;;;;:22:::1;::::0;::::1;::::0;:34;;;;;-1:-1:-1;;127321:34:0;;;;;;;:22;:34;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;127251:112:::0;:::o;124391:456::-;124657:7;30073:1;30836:7;;:19;;30828:63;;;;;-1:-1:-1;;;30828:63:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;30828:63:0;;;;;;;;;;;;;;;30073:1;30969:7;:18;36722:8:::1;:6;:8::i;:::-;36721:9;36713:38;;;::::0;;-1:-1:-1;;;36713:38:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;36713:38:0;;;;;;;;;;;;;::::1;;124629:8:::2;114576;114557:15;:27;;114549:56;;;::::0;;-1:-1:-1;;;114549:56:0;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;114549:56:0;;;;;;;;;;;;;::::2;;124702:137:::3;::::0;;-1:-1:-1;;;124702:137:0;;:11:::3;:137;::::0;::::3;::::0;;;;;;;::::3;::::0;::::3;::::0;;;;;;;;;;;;:35:::3;::::0;::::3;::::0;:137;;;;;::::3;::::0;;;;;;;;:35;:137;::::3;;::::0;::::3;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;::::0;::::3;;-1:-1:-1::0;124702:137:0;30029:1;31148:7;:22;124702:137;124391:456;-1:-1:-1;;;;;;124391:456:0:o;38446:87::-;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;38495:30:::1;:28;:30::i;:::-;38446:87::o:0;120847:186::-;120980:45;;;-1:-1:-1;;;120980:45:0;;:11;:45;;;;-1:-1:-1;;;;;120980:45:0;;;;;;;;120948:7;;120980:39;;;;:45;;;;;;;;;;;;;;;:39;:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;120980:45:0;;120847:186;-1:-1:-1;;120847:186:0:o;122696:319::-;122931:7;30073:1;30836:7;;:19;;30828:63;;;;;-1:-1:-1;;;30828:63:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;30828:63:0;;;;;;;;;;;;;;;30073:1;30969:7;:18;36722:8:::1;:6;:8::i;:::-;36721:9;36713:38;;;::::0;;-1:-1:-1;;;36713:38:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;36713:38:0;;;;;;;;;;;;;::::1;;122903:8:::2;114576;114557:15;:27;;114549:56;;;::::0;;-1:-1:-1;;;114549:56:0;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;114549:56:0;;;;;;;;;;;;;::::2;;122963:11:::3;:24;;;;122988:7;;122997:9;122963:44;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::3;128080:128:::0;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;128162:38:::1;::::0;;-1:-1:-1;;;128162:38:0;;:11:::1;:38;::::0;::::1;::::0;;;;;;;;;;;;;;;:17:::1;::::0;::::1;::::0;:38;;;;;-1:-1:-1;;128162:38:0;;;;;;;:17;:38;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;128080:128:::0;;:::o;36396:86::-;36467:7;;;;36396:86;:::o;133329:1712::-;30073:1;30836:7;;:19;;30828:63;;;;;-1:-1:-1;;;30828:63:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;30828:63:0;;;;;;;;;;;;;;;30073:1;30969:7;:18;133493:16:::1;133512:29;133534:5:::0;133512:13:::1;:29::i;:::-;133493:48;;133552:32;133587:5;-1:-1:-1::0;;;;;133587:15:0::1;;133611:4;133587:30;;;;;;;;;;;;;-1:-1:-1::0;;;;;133587:30:0::1;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;133587:30:0;133702:20;:32;;133587:30;;-1:-1:-1;133628:29:0::1;::::0;133673:62:::1;::::0;133702:20;:32:::1;::::0;::::1;::::0;;::::1;;;;;;;;;;;;;133673:24;:28;;:62;;;;:::i;:::-;133628:107;;133777:1;133768:6;:10;:48;;;;;133810:6;133782:24;:34;;133768:48;133746:112;;;::::0;;-1:-1:-1;;;133746:112:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;133746:112:0;;;;;;;;;;;;;::::1;;133953:17;133973:38;134005:5;133973:27;133984:15;;133973:6;:10;;:27;;;;:::i;:::-;:31:::0;::::1;:38::i;:::-;133953:58;;134096:19;134118:45;134157:5;134118:34;134132:19;;134118:9;:13;;:34;;;;:::i;:45::-;134096:67;;134194:1;134182:9;:13;134174:57;;;::::0;;-1:-1:-1;;;134174:57:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;::::1;;134296:36;-1:-1:-1::0;;;;;134296:18:0;::::1;134315:8:::0;134325:6;134296:18:::1;:36::i;:::-;134414:8;-1:-1:-1::0;;;;;134395:45:0::1;;134463:4;134491:5;134512:6;134533:9;134557:6;134395:179;;;;;;;;;;;;;-1:-1:-1::0;;;;;134395:179:0::1;;;;;;-1:-1:-1::0;;;;;134395:179:0::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;;::::1;::::0;;;::::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;134587:31;134634:57;134669:21;134634:5;-1:-1:-1::0;;;;;134634:15:0::1;;134658:4;134634:30;;;;;;;;;;;;;-1:-1:-1::0;;;;;134634:30:0::1;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;134634:30:0;;:34:::1;:57::i;:::-;134587:104:::0;-1:-1:-1;134751:39:0::1;:24:::0;134780:9;134751:28:::1;:39::i;:::-;134724:23;:66;;134702:140;;;::::0;;-1:-1:-1;;;134702:140:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;::::1;;134890:64;:23:::0;134932:11;134890:27:::1;:64::i;:::-;134855:20:::0;:32;;::::1;::::0;::::1;::::0;;::::1;;;;;;::::0;;;::::1;::::0;;;;::::1;:99:::0;;;;134970:63:::1;::::0;;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;;;;-1:-1:-1;;;;;134970:63:0;::::1;::::0;::::1;::::0;;;;;;;;::::1;-1:-1:-1::0;;30029:1:0;31148:7;:22;-1:-1:-1;;;;;;;;133329:1712:0:o;108703:33::-;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;108703:33:0;;:::o;115866:276::-;-1:-1:-1;;;;;115964:26:0;;115932:5;115964:26;;;:12;:26;;;;;;115932:5;;115964:26;;;;;116031:15;115964:26;116031:8;:15::i;:::-;-1:-1:-1;;;;;116023:40:0;;116001:110;;;;;-1:-1:-1;;;116001:110:0;;;;;;;;;;;;-1:-1:-1;;;116001:110:0;;;;;;;;;;;;;;;116129:5;115866:276;-1:-1:-1;;115866:276:0:o;131849:641::-;27671:13;;;;;;;;:33;;;27688:16;:14;:16::i;:::-;27671:50;;;-1:-1:-1;27709:12:0;;;;27708:13;27671:50;27663:109;;;;-1:-1:-1;;;27663:109:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27785:19;27808:13;;;;;;27807:14;27832:101;;;;27867:13;:20;;-1:-1:-1;;;;27867:20:0;;;;;27902:19;27883:4;27902:19;;;27832:101;132174:218:::1;132204:13;132232:8;132255:11;132281:13;132309:2;132326:4;132345:9;132369:12;132174:15;:218::i;:::-;132421:3;132403:15;:21:::0;132467:4:::1;132445:19;:26:::0;27959:68;;;;28010:5;27994:21;;-1:-1:-1;;27994:21:0;;;27959:68;131849:641;;;;;;;;;:::o;34477:148::-;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;34568:6:::1;::::0;34547:40:::1;::::0;34584:1:::1;::::0;-1:-1:-1;;;;;34568:6:0::1;::::0;34547:40:::1;::::0;34584:1;;34547:40:::1;34598:6;:19:::0;;-1:-1:-1;;;;;;34598:19:0::1;::::0;;34477:148::o;119269:219::-;119427:53;;;-1:-1:-1;;;119427:53:0;;:11;:53;;;;-1:-1:-1;;;;;119427:53:0;;;;;;;;;;;;;;119386:16;;119427:36;;;;:53;;;;;-1:-1:-1;;119427:53:0;;;;;;;;:36;:53;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;119427:53:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;119427:53:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;119427:53:0;;;;;;;;;;;;-1:-1:-1;119427:53:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;119420:60;;119269:219;;;;:::o;130350:30::-;;;;:::o;115438:192::-;115525:24;:31;115490:6;;115517:39;;;;115509:64;;;;;-1:-1:-1;;;115509:64:0;;;;;;;;;;;;-1:-1:-1;;;115509:64:0;;;;;;;;;;;;;;;115591:24;:31;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;115591:31:0;;115438:192;-1:-1:-1;;115438:192:0:o;38271:83::-;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;38318:28:::1;:26;:28::i;125389:351::-:0;125640:7;30073:1;30836:7;;:19;;30828:63;;;;;-1:-1:-1;;;30828:63:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;30828:63:0;;;;;;;;;;;;;;;30073:1;30969:7;:18;36722:8:::1;:6;:8::i;:::-;36721:9;36713:38;;;::::0;;-1:-1:-1;;;36713:38:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;36713:38:0;;;;;;;;;;;;;::::1;;125612:8:::2;114576;114557:15;:27;;114549:56;;;::::0;;-1:-1:-1;;;114549:56:0;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;114549:56:0;;;;;;;;;;;;;::::2;;125672:11:::3;:36;;;;125709:7;;125718:13;125672:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::3;126974:116:::0;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;127046:36:::1;::::0;;-1:-1:-1;;;127046:36:0;;:11:::1;:36;::::0;::::1;::::0;;;;;;;;;:23:::1;::::0;::::1;::::0;:36;;;;;-1:-1:-1;;127046:36:0;;;;;;;:23;:36;::::1;;::::0;::::1;;;;::::0;::::1;33826:87:::0;33899:6;;-1:-1:-1;;;;;33899:6:0;33826:87;:::o;121867:364::-;122134:7;30073:1;30836:7;;:19;;30828:63;;;;;-1:-1:-1;;;30828:63:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;30828:63:0;;;;;;;;;;;;;;;30073:1;30969:7;:18;36722:8:::1;:6;:8::i;:::-;36721:9;36713:38;;;::::0;;-1:-1:-1;;;36713:38:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;36713:38:0;;;;;;;;;;;;;::::1;;122106:8:::2;114576;114557:15;:27;;114549:56;;;::::0;;-1:-1:-1;;;114549:56:0;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;114549:56:0;;;;;;;;;;;;;::::2;;122166:57:::3;::::0;;-1:-1:-1;;;122166:57:0;;:11:::3;:57;::::0;::::3;::::0;::::3;::::0;;::::3;::::0;;;;;::::3;::::0;;;;;;;;;;;;;;;;;;:16:::3;::::0;::::3;::::0;:57;;;;;::::3;::::0;;;;;;;;:16;:57;::::3;;::::0;::::3;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;::::0;::::3;;-1:-1:-1::0;122166:57:0;30029:1;31148:7;:22;122166:57;121867:364;-1:-1:-1;;;;;;;121867:364:0:o;116685:204::-;116782:24;:31;116746:7;;116774:39;;;;116766:70;;;;;-1:-1:-1;;;116766:70:0;;;;;;;;;;;;-1:-1:-1;;;116766:70:0;;;;;;;;;;;;;;;116854:20;:27;;;;;;;;;;;;;;;;;;;;116847:34;;116685:204;;;:::o;119930:352::-;120153:121;;;-1:-1:-1;;;120153:121:0;;:11;:121;;;;-1:-1:-1;;;;;120153:121:0;;;;;;;;;;;;;;;;;;;;;120085:28;;120153:37;;;;:121;;;;;;;;;;;:37;:121;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;120153:121:0;;119930:352;-1:-1:-1;;;;119930:352:0:o;117597:228::-;117758:59;;;-1:-1:-1;;;117758:59:0;;:11;:59;;;;;;;;;;;;;;;;;;;;;;;;;;117731:7;;117758:25;;;;:59;;;;;;;;;;;;;;;:25;:59;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;117758:59:0;;-1:-1:-1;117597:228:0;;;;;;:::o;127583:142::-;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;127668:49:::1;::::0;;-1:-1:-1;;;127668:49:0;;:11:::1;:49;::::0;::::1;::::0;;;;;;;;;:33:::1;::::0;::::1;::::0;:49;;;;;-1:-1:-1;;127668:49:0;;;;;;;:33;:49;::::1;;::::0;::::1;;;;::::0;::::1;126292:306:::0;126444:19;;-1:-1:-1;;;;;126444:19:0;126422:10;:42;126400:119;;;;;-1:-1:-1;;;126400:119:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;126530:60;;;-1:-1:-1;;;126530:60:0;;:11;:60;;;;-1:-1:-1;;;;;126530:60:0;;;;;;;;;;;;;;:33;;;;:60;;;;;-1:-1:-1;;126530:60:0;;;;;;;:33;:60;;;;;;;;;;128316:82;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;128367:23:::1;::::0;;-1:-1:-1;;;128367:23:0;;:11:::1;:23;::::0;::::1;::::0;;;:21:::1;::::0;::::1;::::0;:23;;;;;-1:-1:-1;;128367:23:0;;;;;;;:21;:23;::::1;;::::0;::::1;;;;::::0;::::1;114831:92:::0;114870:7;114897:11;:16;;;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;116306:138;116399:37;;;-1:-1:-1;;;116399:37:0;;:11;:37;;;;-1:-1:-1;;;;;116399:37:0;;;;;;;;116372:7;;116399:31;;;;:37;;;;;;;;;;;;;;;:31;:37;;;;;;;;;;117052:114;117102:7;117129:11;:27;;;;:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;130549:34;;;;:::o;121272:132::-;121335:7;121362:11;:27;;;;121390:5;121362:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;135397:454;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;135581:1:::1;135560:18;:22;:72;;;;;130678:5;135603:18;:29;;135560:72;:126;;;;;130678:5;135653:22;:33;;135560:126;135538:203;;;::::0;;-1:-1:-1;;;135538:203:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;::::1;;135752:15;:36:::0;;;;135799:19:::1;:44:::0;135397:454::o;34780:244::-;34057:12;:10;:12::i;:::-;-1:-1:-1;;;;;34046:23:0;:7;:5;:7::i;:::-;-1:-1:-1;;;;;34046:23:0;;34038:68;;;;;-1:-1:-1;;;34038:68:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;34038:68:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;34869:22:0;::::1;34861:73;;;;-1:-1:-1::0;;;34861:73:0::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;34971:6;::::0;34950:38:::1;::::0;-1:-1:-1;;;;;34950:38:0;;::::1;::::0;34971:6:::1;::::0;34950:38:::1;::::0;34971:6:::1;::::0;34950:38:::1;34999:6;:17:::0;;-1:-1:-1;;;;;;34999:17:0::1;-1:-1:-1::0;;;;;34999:17:0;;;::::1;::::0;;;::::1;::::0;;34780:244::o;118651:240::-;118797:7;118824:11;:32;;;;118857:7;118866;;118875;118824:59;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;118824:59:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;118824:59:0;;118651:240;-1:-1:-1;;;;;118651:240:0:o;130644:39::-;130678:5;130644:39;:::o;32086:106::-;32174:10;32086:106;:::o;37455:120::-;36999:8;:6;:8::i;:::-;36991:41;;;;;-1:-1:-1;;;36991:41:0;;;;;;;;;;;;-1:-1:-1;;;36991:41:0;;;;;;;;;;;;;;;37514:7:::1;:15:::0;;-1:-1:-1;;37514:15:0::1;::::0;;37545:22:::1;37554:12;:10;:12::i;:::-;37545:22;::::0;;-1:-1:-1;;;;;37545:22:0;;::::1;::::0;;;;;;;::::1;::::0;;::::1;37455:120::o:0;1443:136::-;1501:7;1528:43;1532:1;1535;1528:43;;;;;;;;;;;;;;;;;:3;:43::i;2333:471::-;2391:7;2636:6;2632:47;;-1:-1:-1;2666:1:0;2659:8;;2632:47;2703:5;;;2707:1;2703;:5;:1;2727:5;;;;;:10;2719:56;;;;-1:-1:-1;;;2719:56:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3280:132;3338:7;3365:39;3369:1;3372;3365:39;;;;;;;;;;;;;;;;;:3;:39::i;15972:177::-;16082:58;;;-1:-1:-1;;;;;16082:58:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;16082:58:0;-1:-1:-1;;;16082:58:0;;;16055:86;;16075:5;;16055:19;:86::i;:::-;15972:177;;;:::o;979:181::-;1037:7;1069:5;;;1093:6;;;;1085:46;;;;;-1:-1:-1;;;1085:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;;28127:125;28175:4;28200:44;28238:4;28200:29;:44::i;:::-;28199:45;28192:52;;28127:125;:::o;111350:2957::-;27671:13;;;;;;;;:33;;;27688:16;:14;:16::i;:::-;27671:50;;;-1:-1:-1;27709:12:0;;;;27708:13;27671:50;27663:109;;;;-1:-1:-1;;;27663:109:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27785:19;27808:13;;;;;;27807:14;27832:101;;;;27867:13;:20;;-1:-1:-1;;;;27867:20:0;;;;;27902:19;27883:4;27902:19;;;27832:101;111666:22:::1;:20;:22::i;:::-;111699:24;:22;:24::i;:::-;111822:1;111799:13;:20;:24;111791:62;;;::::0;;-1:-1:-1;;;111791:62:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;::::1;;111896:2;111872:13;:20;:26;;111864:64;;;::::0;;-1:-1:-1;;;111864:64:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;::::1;;111985:8;:15;111961:13;:20;:39;111939:120;;;::::0;;-1:-1:-1;;;111939:120:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;::::1;;112072:37;112126:8;:15;112112:30;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;-1:-1:-1;112112:30:0::1;;112072:70;;112160:7;112155:964;112177:13;:20;112173:1;:24;;;112155:964;;;112223:5;::::0;::::1;::::0;112219:334:::1;;112368:12;:39;112389:13;112403:1;112389:16;;;;;;;;;;;::::0;;::::1;::::0;;;;;;;-1:-1:-1;;;;;112368:39:0::1;::::0;;;::::1;::::0;;;;;;-1:-1:-1;112368:39:0;;::::1;;:44:::0;:109;::::1;;;;112461:13;112475:1;112461:16;;;;;;;;;;;;;;;;-1:-1:-1::0;;;;;112441:36:0::1;:13;112455:1;112441:16;;;;;;;;;;;;;;-1:-1:-1::0;;;;;112441:36:0::1;;;112368:109;112338:199;;;::::0;;-1:-1:-1;;;112338:199:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;112338:199:0;;;;;;;;;;;;;::::1;;112630:1;-1:-1:-1::0;;;;;112593:39:0::1;112601:13;112615:1;112601:16;;;;;;;;;;;;;;;;-1:-1:-1::0;;;;;112593:39:0::1;;;112567:130;;;::::0;;-1:-1:-1;;;112567:130:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;::::1;;64541:2;112738:48;;:8;112747:1;112738:11;;;;;;;;;;;;;;;;:48;;;;112712:136;;;::::0;;-1:-1:-1;;;112712:136:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;::::1;;112933:116;113014:8;113023:1;113014:11;;;;;;;;;;;;;;;;113006:20;;64541:2;112933:42;;:46;;:116;;;;:::i;:::-;112906:2;:143;112863:20;112884:1;112863:23;;;;;;;;;;;;;;;:186;;;::::0;::::1;113106:1;113064:12;:39;113085:13;113099:1;113085:16;;;;;;;;;;;::::0;;::::1;::::0;;;;;;;-1:-1:-1;;;;;113064:39:0::1;::::0;;;::::1;::::0;;;;;;-1:-1:-1;113064:39:0;:43;;-1:-1:-1;;113064:43:0::1;;::::0;;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;112199:3:0::1;112155:964;;;;65608:5;113202:2;:20;113194:51;;;::::0;;-1:-1:-1;;;113194:51:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;113194:51:0;;;;;;;;;;;;;::::1;;64857:5;113264:4;:29;113256:62;;;::::0;;-1:-1:-1;;;113256:62:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;113256:62:0;;;;;;;;;;;;;::::1;;65151:6;113351:9;:35;113329:110;;;::::0;;-1:-1:-1;;;113329:110:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;::::1;;65359:5;113472:12;:41;113450:119;;;::::0;;-1:-1:-1;;;113450:119:0;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;::::1;::::0;;;;;;;;;;;;;::::1;;113672:11;113698:13;64541:2;113646:124;;;;;:::i;:::-;;::::0;::::1;::::0;;;;;;;;;;;;;;;;;;::::1;::::0;;::::1;::::0;;;;;;::::1;::::0;;;;;::::1;;;;;;;::::0;;::::1;::::0;;;::::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;113646:124:0;;::::1;::::0;;;;;;;;::::1;::::0;;::::1;::::0;;::::1;::::0;;;;::::1;;;;;;;;::::0;;::::1;::::0;;;::::1;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;113624:19:0;:146;;-1:-1:-1;;;;;;113624:146:0::1;-1:-1:-1::0;;;;;113624:146:0;;;::::1;::::0;;;::::1;::::0;;113781:40;;::::1;::::0;:24;;:40:::1;::::0;::::1;::::0;::::1;:::i;:::-;-1:-1:-1::0;113832:60:0;;::::1;::::0;:37;;:60:::1;::::0;::::1;::::0;::::1;:::i;:::-;;113940:13;:20;113926:35;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;-1:-1:-1;113926:35:0::1;-1:-1:-1::0;113903:58:0;;::::1;::::0;:20;;:58:::1;::::0;;::::1;::::0;::::1;:::i;:::-;-1:-1:-1::0;113995:29:0::1;:2:::0;65566:3:::1;113995:6;:29::i;:::-;113972:11;:52:::0;114057:29:::1;:2:::0;65566:3:::1;114057:6;:29::i;:::-;114035:19:::0;:51;-1:-1:-1;114124:1:0::1;114097:24:::0;:28;;;114136:23;:27;114174:19;:26;;;114211:20;:32;;;114254:30;:45;;;27959:68;;;;28010:5;27994:21;;-1:-1:-1;;27994:21:0;;;111350:2957;;;;;;;;;:::o;37196:118::-;36722:8;:6;:8::i;:::-;36721:9;36713:38;;;;;-1:-1:-1;;;36713:38:0;;;;;;;;;;;;-1:-1:-1;;;36713:38:0;;;;;;;;;;;;;;;37256:7:::1;:14:::0;;-1:-1:-1;;37256:14:0::1;37266:4;37256:14;::::0;;37286:20:::1;37293:12;:10;:12::i;1882:192::-:0;1968:7;2004:12;1996:6;;;;1988:29;;;;-1:-1:-1;;;1988:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;2040:5:0;;;1882:192::o;3908:278::-;3994:7;4029:12;4022:5;4014:28;;;;-1:-1:-1;;;4014:28:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4053:9;4069:1;4065;:5;;;;;;;3908:278;-1:-1:-1;;;;;3908:278:0:o;18277:761::-;18701:23;18727:69;18755:4;18727:69;;;;;;;;;;;;;;;;;18735:5;-1:-1:-1;;;;;18727:27:0;;;:69;;;;;:::i;:::-;18811:17;;18701:95;;-1:-1:-1;18811:21:0;18807:224;;18953:10;18942:30;;;;;;;;;;;;;;;-1:-1:-1;18942:30:0;18934:85;;;;-1:-1:-1;;;18934:85:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19862:422;20229:20;20268:8;;;19862:422::o;38010:173::-;27671:13;;;;;;;;:33;;;27688:16;:14;:16::i;:::-;27671:50;;;-1:-1:-1;27709:12:0;;;;27708:13;27671:50;27663:109;;;;-1:-1:-1;;;27663:109:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27785:19;27808:13;;;;;;27807:14;27832:101;;;;27867:13;:20;;-1:-1:-1;;;;27867:20:0;;;;;27902:19;27883:4;27902:19;;;27832:101;38074:26:::1;:24;:26::i;:::-;38111;:24;:26::i;:::-;38148:27;:25;:27::i;:::-;27963:14:::0;27959:68;;;28010:5;27994:21;;-1:-1:-1;;27994:21:0;;;27959:68;38010:173;:::o;30115:108::-;27671:13;;;;;;;;:33;;;27688:16;:14;:16::i;:::-;27671:50;;;-1:-1:-1;27709:12:0;;;;27708:13;27671:50;27663:109;;;;-1:-1:-1;;;27663:109:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27785:19;27808:13;;;;;;27807:14;27832:101;;;;27867:13;:20;;-1:-1:-1;;;;27867:20:0;;;;;27902:19;27883:4;27902:19;;;27832:101;30181:34:::1;:32;:34::i;11960:195::-:0;12063:12;12095:52;12117:6;12125:4;12131:1;12134:12;12095:21;:52::i;:::-;12088:59;11960:195;-1:-1:-1;;;;11960:195:0:o;32015:65::-;27671:13;;;;;;;;:33;;;27688:16;:14;:16::i;:::-;27671:50;;;-1:-1:-1;27709:12:0;;;;27708:13;27671:50;27663:109;;;;-1:-1:-1;;;27663:109:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27785:19;27808:13;;;;;;27807:14;27832:101;;;;27867:13;:20;;-1:-1:-1;;;;27867:20:0;;;;;27902:19;27883:4;27902:19;;;27963:14;27959:68;;;28010:5;27994:21;;-1:-1:-1;;27994:21:0;;;32015:65;:::o;33549:196::-;27671:13;;;;;;;;:33;;;27688:16;:14;:16::i;:::-;27671:50;;;-1:-1:-1;27709:12:0;;;;27708:13;27671:50;27663:109;;;;-1:-1:-1;;;27663:109:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27785:19;27808:13;;;;;;27807:14;27832:101;;;;27867:13;:20;;-1:-1:-1;;;;27867:20:0;;;;;27902:19;27883:4;27902:19;;;27832:101;33617:17:::1;33637:12;:10;:12::i;:::-;33660:6;:18:::0;;-1:-1:-1;;;;;;33660:18:0::1;-1:-1:-1::0;;;;;33660:18:0;::::1;::::0;;::::1;::::0;;;33694:43:::1;::::0;33660:18;;-1:-1:-1;33660:18:0;-1:-1:-1;;33694:43:0::1;::::0;-1:-1:-1;;33694:43:0::1;27945:1;27963:14:::0;27959:68;;;28010:5;27994:21;;-1:-1:-1;;27994:21:0;;;33549:196;:::o;36204:92::-;27671:13;;;;;;;;:33;;;27688:16;:14;:16::i;:::-;27671:50;;;-1:-1:-1;27709:12:0;;;;27708:13;27671:50;27663:109;;;;-1:-1:-1;;;27663:109:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27785:19;27808:13;;;;;;27807:14;27832:101;;;;27867:13;:20;;-1:-1:-1;;;;27867:20:0;;;;;27902:19;27883:4;27902:19;;;27832:101;36273:7:::1;:15:::0;;-1:-1:-1;;36273:15:0::1;::::0;;27959:68;;;;28010:5;27994:21;;-1:-1:-1;;27994:21:0;;;36204:92;:::o;30231:106::-;27671:13;;;;;;;;:33;;;27688:16;:14;:16::i;:::-;27671:50;;;-1:-1:-1;27709:12:0;;;;27708:13;27671:50;27663:109;;;;-1:-1:-1;;;27663:109:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27785:19;27808:13;;;;;;27807:14;27832:101;;;;27867:13;:20;;-1:-1:-1;;;;27867:20:0;;;;;27902:19;27883:4;27902:19;;;27832:101;30029:1:::1;30307:7;:22:::0;27959:68;;;;28010:5;27994:21;;-1:-1:-1;;27994:21:0;;;30231:106;:::o;13012:530::-;13139:12;13197:5;13172:21;:30;;13164:81;;;;-1:-1:-1;;;13164:81:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13264:18;13275:6;13264:10;:18::i;:::-;13256:60;;;;;-1:-1:-1;;;13256:60:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;13390:12;13404:23;13431:6;-1:-1:-1;;;;;13431:11:0;13451:5;13459:4;13431:33;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;13431:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13389:75;;;;13482:52;13500:7;13509:10;13521:12;13482:17;:52::i;:::-;13475:59;13012:530;-1:-1:-1;;;;;;;13012:530:0:o;14548:742::-;14663:12;14692:7;14688:595;;;-1:-1:-1;14723:10:0;14716:17;;14688:595;14837:17;;:21;14833:439;;15100:10;15094:17;15161:15;15148:10;15144:2;15140:19;15133:44;15048:148;15236:20;;-1:-1:-1;;;15236:20:0;;;;;;;;;;;;;;;;;15243:12;;15236:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Swarm Source
ipfs://bb1f3710ea2222062f6ceb513fc425261868ab3f070035c57887d01b3bbf1135
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.