Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
DynamicPoolV2
Compiler Version
v0.8.18+commit.87f61d96
Optimization Enabled:
Yes with 1000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.14; import '../libraries/DSMath.sol'; import '../interfaces/IRelativePriceProvider.sol'; import './HighCovRatioFeePoolV2.sol'; /** * @title Dynamic Pool V2 * @notice Manages deposits, withdrawals and swaps. Holds a mapping of assets and parameters. * @dev Supports dynamic assets. Assume r* to be close to 1. * Be aware that r* changes when the relative price of the asset updates * Change log: * - add `gap` to prevent storage collision for future upgrades * - Inherite from `HighCovRatioFeePoolV2` instead of `Pool` */ contract DynamicPoolV2 is HighCovRatioFeePoolV2 { using DSMath for uint256; using SignedSafeMath for int256; uint256[50] private gap; /** * @notice multiply / divide the cash, liability and amount of a swap by relative price * Invariant: D = Sum of P_i * L_i * (r_i - A / r_i) */ function _quoteFactor(IAsset fromAsset, IAsset toAsset) internal view override returns (uint256) { uint256 fromAssetRelativePrice = IRelativePriceProvider(address(fromAsset)).getRelativePrice(); // theoretically we should multiply toCash, toLiability and idealToAmount by toAssetRelativePrice // however we simplify the calculation by dividing "from amounts" by toAssetRelativePrice uint256 toAssetRelativePrice = IRelativePriceProvider(address(toAsset)).getRelativePrice(); return (1e18 * fromAssetRelativePrice) / toAssetRelativePrice; } /** * @dev Invariant: D = Sum of P_i * L_i * (r_i - A / r_i) */ function _globalInvariantFunc() internal view override returns (int256 D, int256 SL) { int256 A = int256(ampFactor); for (uint256 i; i < _sizeOfAssetList(); ++i) { IAsset asset = _getAsset(_getKeyAtIndex(i)); // overflow is unrealistic int256 A_i = int256(uint256(asset.cash())); int256 L_i = int256(uint256(asset.liability())); int256 P_i = int256(IRelativePriceProvider(address(asset)).getRelativePrice()); // Assume when L_i == 0, A_i always == 0 if (L_i == 0) { // avoid division of 0 continue; } int256 r_i = A_i.wdiv(L_i); SL += P_i.wmul(L_i); D += P_i.wmul(L_i).wmul(r_i - A.wdiv(r_i)); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @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 onlyInitializing { __Ownable_init_unchained(); } function __Ownable_init_unchained() internal onlyInitializing { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(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"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @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 proxied contracts do not make use of 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. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * 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 {ERC1967Proxy-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. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized != type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract 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 onlyInitializing { __Pausable_init_unchained(); } function __Pausable_init_unchained() internal onlyInitializing { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { 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()); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @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 onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { _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 making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == _ENTERED; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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"); (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 functionCallWithValue(target, data, 0, "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"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, 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) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // 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 /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @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 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 onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @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() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(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"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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); /** * @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 `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ 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' 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)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to * 0 before setting it to a non-zero value. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @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"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @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). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // 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 cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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"); (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 functionCallWithValue(target, data, 0, "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"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, 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) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // 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 /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^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 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) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.5; import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; interface IAsset is IERC20 { function underlyingToken() external view returns (address); function pool() external view returns (address); function cash() external view returns (uint120); function liability() external view returns (uint120); function decimals() external view returns (uint8); function underlyingTokenDecimals() external view returns (uint8); function setPool(address pool_) external; function underlyingTokenBalance() external view returns (uint256); function transferUnderlyingToken(address to, uint256 amount) external; function mint(address to, uint256 amount) external; function burn(address to, uint256 amount) external; function addCash(uint256 amount) external; function removeCash(uint256 amount) external; function addLiability(uint256 amount) external; function removeLiability(uint256 amount) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.5; interface IPool { function getTokens() external view returns (address[] memory); function addressOfAsset(address token) external view returns (address); function deposit( address token, uint256 amount, uint256 minimumLiquidity, address to, uint256 deadline, bool shouldStake ) external returns (uint256 liquidity); function withdraw( address token, uint256 liquidity, uint256 minimumAmount, address to, uint256 deadline ) external returns (uint256 amount); function withdrawFromOtherAsset( address fromToken, address toToken, uint256 liquidity, uint256 minimumAmount, address to, uint256 deadline ) external returns (uint256 amount); function swap( address fromToken, address toToken, uint256 fromAmount, uint256 minimumToAmount, address to, uint256 deadline ) external returns (uint256 actualToAmount, uint256 haircut); function quotePotentialDeposit( address token, uint256 amount ) external view returns (uint256 liquidity, uint256 reward); function quotePotentialSwap( address fromToken, address toToken, int256 fromAmount ) external view returns (uint256 potentialOutcome, uint256 haircut); function quotePotentialWithdraw( address token, uint256 liquidity ) external view returns (uint256 amount, uint256 fee); function quoteAmountIn( address fromToken, address toToken, int256 toAmount ) external view returns (uint256 amountIn, uint256 haircut); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.5; interface IRelativePriceProvider { /** * @notice get the relative price in WAD */ function getRelativePrice() external view returns (uint256); }
// SPDX-License-Identifier: GPL-3.0 /// math.sol -- mixin for inline numerical wizardry // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.8.5; library DSMath { uint256 public constant WAD = 10 ** 18; // Babylonian Method function sqrt(uint256 y) internal pure returns (uint256 z) { if (y > 3) { z = y; uint256 x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } //rounds to zero if x*y < WAD / 2 function wmul(uint256 x, uint256 y) internal pure returns (uint256) { return ((x * y) + (WAD / 2)) / WAD; } function wdiv(uint256 x, uint256 y) internal pure returns (uint256) { return ((x * WAD) + (y / 2)) / y; } // Convert x to WAD (18 decimals) from d decimals. function toWad(uint256 x, uint8 d) internal pure returns (uint256) { if (d < 18) { return x * 10 ** (18 - d); } else if (d > 18) { return (x / (10 ** (d - 18))); } return x; } // Convert x from WAD (18 decimals) to d decimals. function fromWad(uint256 x, uint8 d) internal pure returns (uint256) { if (d < 18) { return (x / (10 ** (18 - d))); } else if (d > 18) { return x * 10 ** (d - 18); } return x; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.3.2 (utils/math/SignedSafeMath.sol) pragma solidity ^0.8.5; /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler * now has built in overflow checking. */ library SignedSafeMath { int256 public constant WAD = 10 ** 18; //rounds to zero if x*y < WAD / 2 function wdiv(int256 x, int256 y) internal pure returns (int256) { return ((x * WAD) + (y / 2)) / y; } //rounds to zero if x*y < WAD / 2 function wmul(int256 x, int256 y) internal pure returns (int256) { return ((x * y) + (WAD / 2)) / WAD; } // Babylonian Method (typecast as int) function sqrt(int256 y) internal pure returns (int256 z) { if (y > 3) { z = y; int256 x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } // Babylonian Method with initial guess (typecast as int) function sqrt(int256 y, int256 guess) internal pure returns (int256 z) { if (y > 3) { if (guess > 0 && guess <= y) { z = guess; } else if (guess < 0 && -guess <= y) { z = -guess; } else { z = y; } int256 x = (y / z + z) / 2; while (x != z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } // Convert x to WAD (18 decimals) from d decimals. function toWad(int256 x, uint8 d) internal pure returns (int256) { if (d < 18) { return x * int256(10 ** (18 - d)); } else if (d > 18) { return (x / int256(10 ** (d - 18))); } return x; } // Convert x from WAD (18 decimals) to d decimals. function fromWad(int256 x, uint8 d) internal pure returns (int256) { if (d < 18) { return (x / int256(10 ** (18 - d))); } else if (d > 18) { return x * int256(10 ** (d - 18)); } return x; } function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, 'value must be positive'); return uint256(value); } function toInt256(uint256 value) internal pure returns (int256) { require(value <= uint256(type(int256).max), 'value must be positive'); return int256(value); } function abs(int256 value) internal pure returns (uint256) { if (value < 0) { return uint256(-value); } else { return uint256(value); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.5; import '../libraries/DSMath.sol'; import '../libraries/SignedSafeMath.sol'; /** * @title CoreV2 * @notice Handles math operations of Wombat protocol. Assume all params are signed integer with 18 decimals * @dev Uses OpenZeppelin's SignedSafeMath and DSMath's WAD for calculations. */ contract CoreV2 { using DSMath for uint256; using SignedSafeMath for int256; int256 internal constant WAD_I = 10 ** 18; uint256 internal constant WAD = 10 ** 18; error CORE_UNDERFLOW(); /** * @notice Core Wombat stableswap equation * @dev This function always returns >= 0 * @param Ax asset of token x * @param Ay asset of token y * @param Lx liability of token x * @param Ly liability of token y * @param Dx delta x, i.e. token x amount inputted * @param A amplification factor * @return quote The quote for amount of token y swapped for token x amount inputted */ function _swapQuoteFunc( int256 Ax, int256 Ay, int256 Lx, int256 Ly, int256 Dx, int256 A ) internal pure returns (uint256 quote) { if (Lx == 0 || Ly == 0) { // in case div of 0 revert CORE_UNDERFLOW(); } int256 D = Ax + Ay - A.wmul((Lx * Lx) / Ax + (Ly * Ly) / Ay); // flattened _invariantFunc int256 rx_ = (Ax + Dx).wdiv(Lx); int256 b = (Lx * (rx_ - A.wdiv(rx_))) / Ly - D.wdiv(Ly); // flattened _coefficientFunc int256 ry_ = _solveQuad(b, A); int256 Dy = Ly.wmul(ry_) - Ay; if (Dy < 0) { quote = uint256(-Dy); } else { quote = uint256(Dy); } } /** * @notice Solve quadratic equation * @dev This function always returns >= 0 * @param b quadratic equation b coefficient * @param c quadratic equation c coefficient * @return x */ function _solveQuad(int256 b, int256 c) internal pure returns (int256) { return (((b * b) + (c * 4 * WAD_I)).sqrt(b) - b) / 2; } /** * @notice Equation to get invariant constant between token x and token y * @dev This function always returns >= 0 * @param Lx liability of token x * @param rx cov ratio of token x * @param Ly liability of token x * @param ry cov ratio of token y * @param A amplification factor * @return The invariant constant between token x and token y ("D") */ function _invariantFunc(int256 Lx, int256 rx, int256 Ly, int256 ry, int256 A) internal pure returns (int256) { int256 a = Lx.wmul(rx) + Ly.wmul(ry); int256 b = A.wmul(Lx.wdiv(rx) + Ly.wdiv(ry)); return a - b; } /** * @notice Equation to get quadratic equation b coefficient * @dev This function can return >= 0 or <= 0 * @param Lx liability of token x * @param Ly liability of token y * @param rx_ new asset coverage ratio of token x * @param D invariant constant * @param A amplification factor * @return The quadratic equation b coefficient ("b") */ function _coefficientFunc(int256 Lx, int256 Ly, int256 rx_, int256 D, int256 A) internal pure returns (int256) { return Lx.wmul(rx_ - A.wdiv(rx_)).wdiv(Ly) - D.wdiv(Ly); } /** * @return v positive value indicates a reward and negative value indicates a fee */ function depositRewardImpl( int256 D, int256 SL, int256 delta_i, int256 A_i, int256 L_i, int256 A ) internal pure returns (int256 v) { if (L_i == 0) { // early return in case of div of 0 return 0; } if (delta_i + SL == 0) { return L_i - A_i; } int256 r_i_ = _targetedCovRatio(SL, delta_i, A_i, L_i, D, A); v = A_i + delta_i - (L_i + delta_i).wmul(r_i_); } /** * @dev should be used only when r* = 1 */ function withdrawalAmountInEquilImpl( int256 delta_i, int256 A_i, int256 L_i, int256 A ) internal pure returns (int256 amount) { int256 L_i_ = L_i + delta_i; int256 r_i = A_i.wdiv(L_i); int256 rho = L_i.wmul(r_i - A.wdiv(r_i)); int256 beta = (rho + delta_i.wmul(WAD_I - A)) / 2; int256 A_i_ = beta + (beta * beta + A.wmul(L_i_ * L_i_)).sqrt(beta); amount = A_i - A_i_; } /** * @notice return the deposit reward in token amount when target liquidity (LP amount) is known */ function exactDepositLiquidityInEquilImpl( int256 D_i, int256 A_i, int256 L_i, int256 A ) internal pure returns (int256 liquidity) { if (L_i == 0) { // if this is a deposit, there is no reward/fee // if this is a withdrawal, it should have been reverted return D_i; } if (A_i + D_i < 0) { // impossible revert CORE_UNDERFLOW(); } int256 r_i = A_i.wdiv(L_i); int256 k = D_i + A_i; int256 b = k.wmul(WAD_I - A) + 2 * A.wmul(L_i); int256 c = k.wmul(A_i - (A * L_i) / r_i) - k.wmul(k) + A.wmul(L_i).wmul(L_i); int256 l = b * b - 4 * A * c; return (-b + l.sqrt(b)).wdiv(A) / 2; } function _targetedCovRatio( int256 SL, int256 delta_i, int256 A_i, int256 L_i, int256 D, int256 A ) internal pure returns (int256 r_i_) { int256 r_i = A_i.wdiv(L_i); int256 er = _equilCovRatio(D, SL, A); int256 er_ = _newEquilCovRatio(er, SL, delta_i); int256 D_ = _newInvariantFunc(er_, A, SL, delta_i); // Summation of k∈T\{i} is D - L_i.wmul(r_i - A.wdiv(r_i)) int256 b_ = (D - A_i + (L_i * A) / r_i - D_).wdiv(L_i + delta_i); r_i_ = _solveQuad(b_, A); } function _equilCovRatio(int256 D, int256 SL, int256 A) internal pure returns (int256 er) { int256 b = -(D.wdiv(SL)); er = _solveQuad(b, A); } function _newEquilCovRatio(int256 er, int256 SL, int256 delta_i) internal pure returns (int256 er_) { er_ = (delta_i + SL.wmul(er)).wdiv(delta_i + SL); } function _newInvariantFunc(int256 er_, int256 A, int256 SL, int256 delta_i) internal pure returns (int256 D_) { D_ = (SL + delta_i).wmul(er_ - A.wdiv(er_)); } /** * @notice From Yellow Paper (Haircut). * @dev Applies haircut rate to amount * @param amount The amount that will receive the discount * @param rate The rate to be applied * @return The result of operation. */ function _haircut(uint256 amount, uint256 rate) internal pure returns (uint256) { return amount.wmul(rate); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.5; import '../libraries/DSMath.sol'; import './PoolV2.sol'; /** * @title HighCovRatioFeePoolV2 * @dev Pool with high cov ratio fee protection * Change log: * - add `gap` to prevent storage collision for future upgrades */ contract HighCovRatioFeePoolV2 is PoolV2 { using DSMath for uint256; uint128 public startCovRatio; // 1.5 uint128 public endCovRatio; // 1.8 uint256[50] private gap; error WOMBAT_COV_RATIO_LIMIT_EXCEEDED(); function initialize(uint256 ampFactor_, uint256 haircutRate_) public override { super.initialize(ampFactor_, haircutRate_); startCovRatio = 15e17; endCovRatio = 18e17; } function setCovRatioFeeParam(uint128 startCovRatio_, uint128 endCovRatio_) external onlyOwner { if (startCovRatio_ < 1e18 || startCovRatio_ > endCovRatio_) revert WOMBAT_INVALID_VALUE(); startCovRatio = startCovRatio_; endCovRatio = endCovRatio_; } /** * @notice Calculate the high cov ratio fee in the to-asset in a swap. * @dev When cov ratio is in the range [startCovRatio, endCovRatio], the marginal cov ratio is * (r - startCovRatio) / (endCovRatio - startCovRatio). Here we approximate the high cov ratio cut * by calculating the "average" fee. * Note: `finalCovRatio` should be greater than `initCovRatio` */ function _highCovRatioFee(uint256 initCovRatio, uint256 finalCovRatio) internal view returns (uint256 fee) { if (finalCovRatio > endCovRatio) { // invalid swap revert WOMBAT_COV_RATIO_LIMIT_EXCEEDED(); } else if (finalCovRatio <= startCovRatio || finalCovRatio <= initCovRatio) { return 0; } unchecked { // 1. Calculate the area of fee(r) = (r - startCovRatio) / (endCovRatio - startCovRatio) // when r increase from initCovRatio to finalCovRatio // 2. Then multiply it by (endCovRatio - startCovRatio) / (finalCovRatio - initCovRatio) // to get the average fee over the range uint256 a = initCovRatio <= startCovRatio ? 0 : (initCovRatio - startCovRatio) * (initCovRatio - startCovRatio); uint256 b = (finalCovRatio - startCovRatio) * (finalCovRatio - startCovRatio); fee = ((b - a) / (finalCovRatio - initCovRatio) / 2).wdiv(endCovRatio - startCovRatio); } } /** * @dev Exact output swap (fromAmount < 0) should be only used by off-chain quoting function as it is a gas monster */ function _quoteFrom( IAsset fromAsset, IAsset toAsset, int256 fromAmount ) internal view override returns (uint256 actualToAmount, uint256 haircut) { (actualToAmount, haircut) = super._quoteFrom(fromAsset, toAsset, fromAmount); if (fromAmount >= 0) { // normal quote uint256 fromAssetCash = fromAsset.cash(); uint256 fromAssetLiability = fromAsset.liability(); uint256 finalFromAssetCovRatio = (fromAssetCash + uint256(fromAmount)).wdiv(fromAssetLiability); if (finalFromAssetCovRatio > startCovRatio) { // charge high cov ratio fee uint256 highCovRatioFee = _highCovRatioFee( fromAssetCash.wdiv(fromAssetLiability), finalFromAssetCovRatio ).wmul(actualToAmount); actualToAmount -= highCovRatioFee; unchecked { haircut += highCovRatioFee; } } } else { // reverse quote uint256 toAssetCash = toAsset.cash(); uint256 toAssetLiability = toAsset.liability(); uint256 finalToAssetCovRatio = (toAssetCash + uint256(actualToAmount)).wdiv(toAssetLiability); if (finalToAssetCovRatio <= startCovRatio) { // happy path: no high cov ratio fee is charged return (actualToAmount, haircut); } else if (toAssetCash.wdiv(toAssetLiability) >= endCovRatio) { // the to-asset exceeds it's cov ratio limit, further swap to increase cov ratio is impossible revert WOMBAT_COV_RATIO_LIMIT_EXCEEDED(); } // reverse quote: cov ratio of the to-asset exceed endCovRatio. direct reverse quote is not supported // we binary search for a upper bound actualToAmount = _findUpperBound(toAsset, fromAsset, uint256(-fromAmount)); (, haircut) = _quoteFrom(toAsset, fromAsset, int256(actualToAmount)); } } /** * @notice Binary search to find the upper bound of `fromAmount` required to swap `fromAsset` to `toAmount` of `toAsset` * @dev This function should only used as off-chain view function as it is a gas monster */ function _findUpperBound( IAsset fromAsset, IAsset toAsset, uint256 toAmount ) internal view returns (uint256 upperBound) { uint8 decimals = fromAsset.underlyingTokenDecimals(); uint256 toWadFactor = DSMath.toWad(1, decimals); // the search value uses the same number of digits as the token uint256 high = (uint256(fromAsset.liability()).wmul(endCovRatio) - fromAsset.cash()).fromWad(decimals); uint256 low = 1; // verify `high` is a valid upper bound uint256 quote; (quote, ) = _quoteFrom(fromAsset, toAsset, int256(high * toWadFactor)); if (quote < toAmount) revert WOMBAT_COV_RATIO_LIMIT_EXCEEDED(); // Note: we might limit the maximum number of rounds if the request is always rejected by the RPC server while (low < high) { unchecked { uint256 mid = (low + high) / 2; (quote, ) = _quoteFrom(fromAsset, toAsset, int256(mid * toWadFactor)); if (quote >= toAmount) { high = mid; } else { low = mid + 1; } } } return high * toWadFactor; } /** * @dev take into account high cov ratio fee */ function quotePotentialWithdrawFromOtherAsset( address fromToken, address toToken, uint256 liquidity ) external view override returns (uint256 amount, uint256 withdrewAmount) { _checkLiquidity(liquidity); _checkSameAddress(fromToken, toToken); (amount, withdrewAmount) = _quotePotentialWithdrawFromOtherAsset(fromToken, toToken, liquidity); IAsset fromAsset = _assetOf(fromToken); IAsset toAsset = _assetOf(toToken); uint256 fromAssetCash = fromAsset.cash() - withdrewAmount; uint256 fromAssetLiability = fromAsset.liability() - liquidity; uint256 finalFromAssetCovRatio = (fromAssetCash + uint256(withdrewAmount)).wdiv(fromAssetLiability); if (finalFromAssetCovRatio > startCovRatio) { uint256 highCovRatioFee = _highCovRatioFee(fromAssetCash.wdiv(fromAssetLiability), finalFromAssetCovRatio) .wmul(amount); amount -= highCovRatioFee; } withdrewAmount = withdrewAmount.fromWad(fromAsset.underlyingTokenDecimals()); amount = amount.fromWad(toAsset.underlyingTokenDecimals()); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.5; /** * @title PausableAssets * @notice Handles assets pause and unpause of Wombat protocol. * @dev Allows pausing and unpausing of deposit and swap operations */ contract PausableAssets { /** * @dev Emitted when the pause is triggered by `account`. */ event PausedAsset(address token, address account); /** * @dev Emitted when the pause is lifted by `account`. */ event UnpausedAsset(address token, address account); // We use the asset's underlying token as the key to check whether an asset is paused. // A pool will never have two assets with the same underlying token. mapping(address => bool) private _pausedAssets; error WOMBAT_ASSET_ALREADY_PAUSED(); error WOMBAT_ASSET_NOT_PAUSED(); /** * @dev Function to return if the asset is paused. * The return value is only useful when true. * When the return value is false, the asset can be either not paused or not exist. */ function isPaused(address token) public view returns (bool) { return _pausedAssets[token]; } /** * @dev Function to make a function callable only when the asset is not paused. * * Requirements: * * - The asset must not be paused. */ function requireAssetNotPaused(address token) internal view { if (_pausedAssets[token]) revert WOMBAT_ASSET_ALREADY_PAUSED(); } /** * @dev Function to make a function callable only when the asset is paused. * * Requirements: * * - The asset must be paused. */ function requireAssetPaused(address token) internal view { if (!_pausedAssets[token]) revert WOMBAT_ASSET_NOT_PAUSED(); } /** * @dev Triggers paused state. * * Requirements: * * - The asset must not be paused. */ function _pauseAsset(address token) internal { requireAssetNotPaused(token); _pausedAssets[token] = true; emit PausedAsset(token, msg.sender); } /** * @dev Returns to normal state. * * Requirements: * * - The asset must be paused. */ function _unpauseAsset(address token) internal { requireAssetPaused(token); _pausedAssets[token] = false; emit UnpausedAsset(token, msg.sender); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.5; import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol'; import '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; import './CoreV2.sol'; import '../interfaces/IAsset.sol'; import './PausableAssets.sol'; import '../../wombat-governance/interfaces/IMasterWombat.sol'; import '../interfaces/IPool.sol'; /** * @title Pool V2 * @notice Manages deposits, withdrawals and swaps. Holds a mapping of assets and parameters. * @dev The main entry-point of Wombat protocol * Note: All variables are 18 decimals, except from that of underlying tokens * Change log: * - add `gap` to prevent storage collision for future upgrades */ contract PoolV2 is Initializable, IPool, OwnableUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable, PausableAssets, CoreV2 { using DSMath for uint256; using SafeERC20 for IERC20; using SignedSafeMath for int256; /// @notice Asset Map struct holds assets struct AssetMap { address[] keys; mapping(address => IAsset) values; mapping(address => uint256) indexOf; } /* Storage */ /// @notice Amplification factor uint256 public ampFactor; /// @notice Haircut rate uint256 public haircutRate; /// @notice Retention ratio: the ratio of haircut that should stay in the pool uint256 public retentionRatio; /// @notice LP dividend ratio : the ratio of haircut that should distribute to LP uint256 public lpDividendRatio; /// @notice The threshold to mint fee (unit: WAD) uint256 public mintFeeThreshold; /// @notice Dev address address public dev; address public feeTo; address public masterWombat; /// @notice Dividend collected by each asset (unit: WAD) mapping(IAsset => uint256) internal _feeCollected; /// @notice A record of assets inside Pool AssetMap internal _assets; // Slots reserved for future use uint128 internal _used1; // Remember to initialize before use. uint128 internal _used2; // Remember to initialize before use. uint256[49] private gap; /* Events */ /// @notice An event thats emitted when an asset is added to Pool event AssetAdded(address indexed token, address indexed asset); /// @notice An event thats emitted when asset is removed from Pool event AssetRemoved(address indexed token, address indexed asset); /// @notice An event thats emitted when a deposit is made to Pool event Deposit(address indexed sender, address token, uint256 amount, uint256 liquidity, address indexed to); /// @notice An event thats emitted when a withdrawal is made from Pool event Withdraw(address indexed sender, address token, uint256 amount, uint256 liquidity, address indexed to); /// @notice An event thats emitted when a swap is made in Pool event Swap( address indexed sender, address fromToken, address toToken, uint256 fromAmount, uint256 toAmount, address indexed to ); event SetDev(address addr); event SetMasterWombat(address addr); event SetFeeTo(address addr); event SetMintFeeThreshold(uint256 value); event SetFee(uint256 lpDividendRatio, uint256 retentionRatio); event SetAmpFactor(uint256 value); event SetHaircutRate(uint256 value); event FillPool(address token, uint256 amount); event TransferTipBucket(address token, uint256 amount, address to); /* Errors */ error WOMBAT_FORBIDDEN(); error WOMBAT_EXPIRED(); error WOMBAT_ASSET_NOT_EXISTS(); error WOMBAT_ASSET_ALREADY_EXIST(); error WOMBAT_ZERO_ADDRESS(); error WOMBAT_ZERO_AMOUNT(); error WOMBAT_ZERO_LIQUIDITY(); error WOMBAT_INVALID_VALUE(); error WOMBAT_SAME_ADDRESS(); error WOMBAT_AMOUNT_TOO_LOW(); error WOMBAT_CASH_NOT_ENOUGH(); /* Pesudo modifiers to safe gas */ function _checkLiquidity(uint256 liquidity) internal pure { if (liquidity == 0) revert WOMBAT_ZERO_LIQUIDITY(); } function _checkAddress(address to) internal pure { if (to == address(0)) revert WOMBAT_ZERO_ADDRESS(); } function _checkSameAddress(address from, address to) internal pure { if (from == to) revert WOMBAT_SAME_ADDRESS(); } function _checkAmount(uint256 minAmt, uint256 amt) internal pure { if (minAmt > amt) revert WOMBAT_AMOUNT_TOO_LOW(); } function _ensure(uint256 deadline) internal view { if (deadline < block.timestamp) revert WOMBAT_EXPIRED(); } function _onlyDev() internal view { if (dev != msg.sender) revert WOMBAT_FORBIDDEN(); } /* Construtor and setters */ /** * @notice Initializes pool. Dev is set to be the account calling this function. */ function initialize(uint256 ampFactor_, uint256 haircutRate_) public virtual initializer { __Ownable_init(); __ReentrancyGuard_init_unchained(); __Pausable_init_unchained(); ampFactor = ampFactor_; haircutRate = haircutRate_; lpDividendRatio = WAD; dev = msg.sender; } /** * Permisioneed functions */ /** * @notice Adds asset to pool, reverts if asset already exists in pool * @param token The address of token * @param asset The address of the Wombat Asset contract */ function addAsset(address token, address asset) external onlyOwner { _checkAddress(asset); _checkAddress(token); if (_containsAsset(token)) revert WOMBAT_ASSET_ALREADY_EXIST(); _assets.values[token] = IAsset(asset); _assets.indexOf[token] = _assets.keys.length; _assets.keys.push(token); emit AssetAdded(token, asset); } /** * @notice Removes asset from asset struct * @dev Can only be called by owner * @param token The address of token to remove */ function removeAsset(address token) external onlyOwner { if (!_containsAsset(token)) revert WOMBAT_ASSET_NOT_EXISTS(); address asset = address(_getAsset(token)); delete _assets.values[token]; uint256 index = _assets.indexOf[token]; uint256 lastIndex = _assets.keys.length - 1; address lastKey = _assets.keys[lastIndex]; _assets.indexOf[lastKey] = index; delete _assets.indexOf[token]; _assets.keys[index] = lastKey; _assets.keys.pop(); emit AssetRemoved(token, asset); } /** * @notice Changes the contract dev. Can only be set by the contract owner. * @param dev_ new contract dev address */ function setDev(address dev_) external onlyOwner { _checkAddress(dev_); dev = dev_; emit SetDev(dev_); } function setMasterWombat(address masterWombat_) external onlyOwner { _checkAddress(masterWombat_); masterWombat = masterWombat_; emit SetMasterWombat(masterWombat_); } /** * @notice Changes the pools amplification factor. Can only be set by the contract owner. * @param ampFactor_ new pool's amplification factor */ function setAmpFactor(uint256 ampFactor_) external onlyOwner { if (ampFactor_ > WAD) revert WOMBAT_INVALID_VALUE(); // ampFactor_ should not be set bigger than 1 ampFactor = ampFactor_; emit SetAmpFactor(ampFactor_); } /** * @notice Changes the pools haircutRate. Can only be set by the contract owner. * @param haircutRate_ new pool's haircutRate_ */ function setHaircutRate(uint256 haircutRate_) external onlyOwner { if (haircutRate_ > WAD) revert WOMBAT_INVALID_VALUE(); // haircutRate_ should not be set bigger than 1 haircutRate = haircutRate_; emit SetHaircutRate(haircutRate_); } function setFee(uint256 lpDividendRatio_, uint256 retentionRatio_) external onlyOwner { unchecked { if (retentionRatio_ + lpDividendRatio_ > WAD) revert WOMBAT_INVALID_VALUE(); } _mintAllFees(); retentionRatio = retentionRatio_; lpDividendRatio = lpDividendRatio_; emit SetFee(lpDividendRatio_, retentionRatio_); } /** * @dev unit of amount should be in WAD */ function transferTipBucket(address token, uint256 amount, address to) external onlyOwner { IAsset asset = _assetOf(token); uint256 tipBucketBal = tipBucketBalance(token); if (amount > tipBucketBal) { // revert if there's not enough amount in the tip bucket revert WOMBAT_INVALID_VALUE(); } asset.transferUnderlyingToken(to, amount.fromWad(asset.underlyingTokenDecimals())); emit TransferTipBucket(token, amount, to); } /** * @notice Changes the fee beneficiary. Can only be set by the contract owner. * This value cannot be set to 0 to avoid unsettled fee. * @param feeTo_ new fee beneficiary */ function setFeeTo(address feeTo_) external onlyOwner { _checkAddress(feeTo_); feeTo = feeTo_; emit SetFeeTo(feeTo_); } /** * @notice Set min fee to mint */ function setMintFeeThreshold(uint256 mintFeeThreshold_) external onlyOwner { mintFeeThreshold = mintFeeThreshold_; emit SetMintFeeThreshold(mintFeeThreshold_); } /** * @dev pause pool, restricting certain operations */ function pause() external { _onlyDev(); _pause(); } /** * @dev unpause pool, enabling certain operations */ function unpause() external { _onlyDev(); _unpause(); } /** * @dev pause asset, restricting deposit and swap operations */ function pauseAsset(address token) external { _onlyDev(); if (!_containsAsset(token)) revert WOMBAT_ASSET_NOT_EXISTS(); _pauseAsset(token); } /** * @dev unpause asset, enabling deposit and swap operations */ function unpauseAsset(address token) external { _onlyDev(); _unpauseAsset(token); } /** * @notice Move fund from tip bucket to the pool to keep r* = 1 as error accumulates * unit of amount should be in WAD */ function fillPool(address token, uint256 amount) external { _onlyDev(); IAsset asset = _assetOf(token); uint256 tipBucketBal = asset.underlyingTokenBalance().toWad(asset.underlyingTokenDecimals()) - asset.cash() - _feeCollected[asset]; if (amount > tipBucketBal) { // revert if there's not enough amount in the tip bucket revert WOMBAT_INVALID_VALUE(); } asset.addCash(amount); emit FillPool(token, amount); } /* Assets */ /** * @notice Return list of tokens in the pool */ function getTokens() external view override returns (address[] memory) { return _assets.keys; } /** * @notice get length of asset list * @return the size of the asset list */ function _sizeOfAssetList() internal view returns (uint256) { return _assets.keys.length; } /** * @notice Gets asset with token address key * @param key The address of token * @return the corresponding asset in state */ function _getAsset(address key) internal view returns (IAsset) { return _assets.values[key]; } /** * @notice Gets key (address) at index * @param index the index * @return the key of index */ function _getKeyAtIndex(uint256 index) internal view returns (address) { return _assets.keys[index]; } /** * @notice Looks if the asset is contained by the list * @param token The address of token to look for * @return bool true if the asset is in asset list, false otherwise */ function _containsAsset(address token) internal view returns (bool) { return _assets.values[token] != IAsset(address(0)); } /** * @notice Gets Asset corresponding to ERC20 token. Reverts if asset does not exists in Pool. * @param token The address of ERC20 token */ function _assetOf(address token) internal view returns (IAsset) { if (!_containsAsset(token)) revert WOMBAT_ASSET_NOT_EXISTS(); return _assets.values[token]; } /** * @notice Gets Asset corresponding to ERC20 token. Reverts if asset does not exists in Pool. * @dev to be used externally * @param token The address of ERC20 token */ function addressOfAsset(address token) external view override returns (address) { return address(_assetOf(token)); } /* Deposit */ /** * This function calculate the exactly amount of liquidity of the deposit. Assumes r* = 1 */ function _exactDepositToInEquil( IAsset asset, uint256 amount ) internal view returns (uint256 lpTokenToMint, uint256 liabilityToMint, uint256 reward) { liabilityToMint = exactDepositLiquidityInEquilImpl( int256(amount), int256(uint256(asset.cash())), int256(uint256(asset.liability())), int256(ampFactor) ).toUint256(); if (liabilityToMint >= amount) { unchecked { reward = liabilityToMint - amount; } } else { // rounding error liabilityToMint = amount; } // Calculate amount of LP to mint : ( deposit + reward ) * TotalAssetSupply / Liability uint256 liability = asset.liability(); lpTokenToMint = (liability == 0 ? liabilityToMint : (liabilityToMint * asset.totalSupply()) / liability); } /** * @notice Deposits asset in Pool * @param asset The asset to be deposited * @param amount The amount to be deposited * @param to The user accountable for deposit, receiving the Wombat assets (lp) * @return liquidity Total asset liquidity minted */ function _deposit( IAsset asset, uint256 amount, uint256 minimumLiquidity, address to ) internal returns (uint256 liquidity) { // collect fee before deposit _mintFeeIfNeeded(asset); uint256 liabilityToMint; (liquidity, liabilityToMint, ) = _exactDepositToInEquil(asset, amount); _checkLiquidity(liquidity); _checkAmount(minimumLiquidity, liquidity); asset.addCash(amount); asset.addLiability(liabilityToMint); asset.mint(to, liquidity); } /** * @notice Deposits amount of tokens into pool ensuring deadline * @dev Asset needs to be created and added to pool before any operation. This function assumes tax free token. * @param token The token address to be deposited * @param amount The amount to be deposited * @param to The user accountable for deposit, receiving the Wombat assets (lp) * @param deadline The deadline to be respected * @return liquidity Total asset liquidity minted */ function deposit( address token, uint256 amount, uint256 minimumLiquidity, address to, uint256 deadline, bool shouldStake ) external override nonReentrant whenNotPaused returns (uint256 liquidity) { if (amount == 0) revert WOMBAT_ZERO_AMOUNT(); _checkAddress(to); _ensure(deadline); requireAssetNotPaused(token); IAsset asset = _assetOf(token); IERC20(token).safeTransferFrom(address(msg.sender), address(asset), amount); if (!shouldStake) { liquidity = _deposit(asset, amount.toWad(asset.underlyingTokenDecimals()), minimumLiquidity, to); } else { _checkAddress(masterWombat); // deposit and stake on behalf of the user liquidity = _deposit(asset, amount.toWad(asset.underlyingTokenDecimals()), minimumLiquidity, address(this)); asset.approve(masterWombat, liquidity); uint256 pid = IMasterWombat(masterWombat).getAssetPid(address(asset)); IMasterWombat(masterWombat).depositFor(pid, liquidity, to); } emit Deposit(msg.sender, token, amount, liquidity, to); } /** * @notice Quotes potential deposit from pool * @dev To be used by frontend * @param token The token to deposit by user * @param amount The amount to deposit * @return liquidity The potential liquidity user would receive * @return reward */ function quotePotentialDeposit( address token, uint256 amount ) external view override returns (uint256 liquidity, uint256 reward) { IAsset asset = _assetOf(token); (liquidity, , reward) = _exactDepositToInEquil(asset, amount.toWad(asset.underlyingTokenDecimals())); } /* Withdraw */ /** * @notice Calculates fee and liability to burn in case of withdrawal * @param asset The asset willing to be withdrawn * @param liquidity The liquidity willing to be withdrawn * @return amount Total amount to be withdrawn from Pool * @return liabilityToBurn Total liability to be burned by Pool * @return fee */ function _withdrawFrom( IAsset asset, uint256 liquidity ) internal view returns (uint256 amount, uint256 liabilityToBurn, uint256 fee) { liabilityToBurn = (asset.liability() * liquidity) / asset.totalSupply(); _checkLiquidity(liabilityToBurn); amount = withdrawalAmountInEquilImpl( -int256(liabilityToBurn), int256(uint256(asset.cash())), int256(uint256(asset.liability())), int256(ampFactor) ).toUint256(); if (liabilityToBurn >= amount) { fee = liabilityToBurn - amount; } else { // rounding error amount = liabilityToBurn; } } /** * @notice Withdraws liquidity amount of asset to `to` address ensuring minimum amount required * @param asset The asset to be withdrawn * @param liquidity The liquidity to be withdrawn * @param minimumAmount The minimum amount that will be accepted by user * @return amount The total amount withdrawn */ function _withdraw(IAsset asset, uint256 liquidity, uint256 minimumAmount) internal returns (uint256 amount) { // collect fee before withdraw _mintFeeIfNeeded(asset); // calculate liabilityToBurn and Fee uint256 liabilityToBurn; (amount, liabilityToBurn, ) = _withdrawFrom(asset, liquidity); _checkAmount(minimumAmount, amount); asset.burn(address(asset), liquidity); asset.removeCash(amount); asset.removeLiability(liabilityToBurn); // revert if cov ratio < 1% to avoid precision error if (asset.liability() > 0 && uint256(asset.cash()).wdiv(asset.liability()) < WAD / 100) revert WOMBAT_FORBIDDEN(); } /** * @notice Withdraws liquidity amount of asset to `to` address ensuring minimum amount required * @param token The token to be withdrawn * @param liquidity The liquidity to be withdrawn * @param minimumAmount The minimum amount that will be accepted by user * @param to The user receiving the withdrawal * @param deadline The deadline to be respected * @return amount The total amount withdrawn */ function withdraw( address token, uint256 liquidity, uint256 minimumAmount, address to, uint256 deadline ) external override nonReentrant whenNotPaused returns (uint256 amount) { _checkLiquidity(liquidity); _checkAddress(to); _ensure(deadline); IAsset asset = _assetOf(token); // request lp token from user IERC20(asset).safeTransferFrom(address(msg.sender), address(asset), liquidity); uint8 decimals = asset.underlyingTokenDecimals(); amount = _withdraw(asset, liquidity, minimumAmount.toWad(decimals)).fromWad(decimals); asset.transferUnderlyingToken(to, amount); emit Withdraw(msg.sender, token, amount, liquidity, to); } /** * @notice Enables withdrawing liquidity from an asset using LP from a different asset * @param fromToken The corresponding token user holds the LP (Asset) from * @param toToken The token wanting to be withdrawn (needs to be well covered) * @param liquidity The liquidity to be withdrawn (in fromToken decimal) * @param minimumAmount The minimum amount that will be accepted by user * @param to The user receiving the withdrawal * @param deadline The deadline to be respected * @return toAmount The total amount withdrawn */ function withdrawFromOtherAsset( address fromToken, address toToken, uint256 liquidity, uint256 minimumAmount, address to, uint256 deadline ) external override nonReentrant whenNotPaused returns (uint256 toAmount) { _checkAddress(to); _checkLiquidity(liquidity); _checkSameAddress(fromToken, toToken); _ensure(deadline); requireAssetNotPaused(fromToken); // Withdraw and swap IAsset fromAsset = _assetOf(fromToken); IAsset toAsset = _assetOf(toToken); IERC20(fromAsset).safeTransferFrom(address(msg.sender), address(fromAsset), liquidity); uint256 fromAmountInWad = _withdraw(fromAsset, liquidity, 0); (toAmount, ) = _swap( fromAsset, toAsset, fromAmountInWad, minimumAmount.toWad(toAsset.underlyingTokenDecimals()) ); toAmount = toAmount.fromWad(toAsset.underlyingTokenDecimals()); toAsset.transferUnderlyingToken(to, toAmount); emit Withdraw(msg.sender, toToken, toAmount, liquidity, to); } /** * @notice Quotes potential withdrawal from pool * @dev To be used by frontend * @param token The token to be withdrawn by user * @param liquidity The liquidity (amount of lp assets) to be withdrawn * @return amount The potential amount user would receive * @return fee The fee that would be applied */ function quotePotentialWithdraw( address token, uint256 liquidity ) external view override returns (uint256 amount, uint256 fee) { _checkLiquidity(liquidity); IAsset asset = _assetOf(token); (amount, , fee) = _withdrawFrom(asset, liquidity); amount = amount.fromWad(asset.underlyingTokenDecimals()); } function _quotePotentialWithdrawFromOtherAsset( address fromToken, address toToken, uint256 liquidity ) internal view returns (uint256 amount, uint256 withdrewAmount) { IAsset fromAsset = _assetOf(fromToken); IAsset toAsset = _assetOf(toToken); // quote withdraw (withdrewAmount, , ) = _withdrawFrom(fromAsset, liquidity); // quote swap uint256 fromCash = fromAsset.cash() - withdrewAmount; uint256 fromLiability = fromAsset.liability() - liquidity; uint256 scaleFactor = _quoteFactor(fromAsset, toAsset); if (scaleFactor != WAD) { // apply scale factor on from-amounts fromCash = (fromCash * scaleFactor) / 1e18; fromLiability = (fromLiability * scaleFactor) / 1e18; withdrewAmount = (withdrewAmount * scaleFactor) / 1e18; } uint256 idealToAmount = _swapQuoteFunc( int256(fromCash), int256(uint256(toAsset.cash())), int256(fromLiability), int256(uint256(toAsset.liability())), int256(withdrewAmount), int256(ampFactor) ); // remove haircut amount = idealToAmount - idealToAmount.wmul(haircutRate); } /** * @notice Quotes potential withdrawal from other asset from the pool * @dev To be used by frontend * @param fromToken The corresponding token user holds the LP (Asset) from * @param toToken The token wanting to be withdrawn (needs to be well covered) * @param liquidity The liquidity (amount of the lp assets) to be withdrawn * @return amount The potential amount user would receive * @return withdrewAmount The amount of the from-token that is withdrew */ function quotePotentialWithdrawFromOtherAsset( address fromToken, address toToken, uint256 liquidity ) external view virtual returns (uint256 amount, uint256 withdrewAmount) { _checkLiquidity(liquidity); _checkSameAddress(fromToken, toToken); (amount, withdrewAmount) = _quotePotentialWithdrawFromOtherAsset(fromToken, toToken, liquidity); IAsset fromAsset = _assetOf(fromToken); IAsset toAsset = _assetOf(toToken); withdrewAmount = withdrewAmount.fromWad(fromAsset.underlyingTokenDecimals()); amount = amount.fromWad(toAsset.underlyingTokenDecimals()); } /* Swap */ /** * @notice Return the scale factor that should applied on from-amounts in a swap given * the from-asset and the to-asset. * @dev not applicable to a plain pool */ function _quoteFactor( IAsset, // fromAsset IAsset // toAsset ) internal view virtual returns (uint256) { // virtual function; do nothing return 1e18; } /** * @notice Quotes the actual amount user would receive in a swap, taking in account slippage and haircut * @param fromAsset The initial asset * @param toAsset The asset wanted by user * @param fromAmount The amount to quote * @return actualToAmount The actual amount user would receive * @return haircut The haircut that will be applied */ function _quoteFrom( IAsset fromAsset, IAsset toAsset, int256 fromAmount ) internal view virtual returns (uint256 actualToAmount, uint256 haircut) { // exact output swap quote should count haircut before swap if (fromAmount < 0) { fromAmount = fromAmount.wdiv(WAD_I - int256(haircutRate)); } uint256 fromCash = uint256(fromAsset.cash()); uint256 fromLiability = uint256(fromAsset.liability()); uint256 toCash = uint256(toAsset.cash()); uint256 scaleFactor = _quoteFactor(fromAsset, toAsset); if (scaleFactor != WAD) { // apply scale factor on from-amounts fromCash = (fromCash * scaleFactor) / 1e18; fromLiability = (fromLiability * scaleFactor) / 1e18; fromAmount = (fromAmount * int256(scaleFactor)) / 1e18; } uint256 idealToAmount = _swapQuoteFunc( int256(fromCash), int256(toCash), int256(fromLiability), int256(uint256(toAsset.liability())), fromAmount, int256(ampFactor) ); if ((fromAmount > 0 && toCash < idealToAmount) || (fromAmount < 0 && fromAsset.cash() < uint256(-fromAmount))) { revert WOMBAT_CASH_NOT_ENOUGH(); } if (fromAmount > 0) { // normal quote haircut = idealToAmount.wmul(haircutRate); actualToAmount = idealToAmount - haircut; } else { // exact output swap quote count haircut in the fromAmount actualToAmount = idealToAmount; haircut = (uint256(-fromAmount)).wmul(haircutRate); } } /** * expect fromAmount and minimumToAmount to be in WAD */ function _swap( IAsset fromAsset, IAsset toAsset, uint256 fromAmount, uint256 minimumToAmount ) internal returns (uint256 actualToAmount, uint256 haircut) { (actualToAmount, haircut) = _quoteFrom(fromAsset, toAsset, int256(fromAmount)); _checkAmount(minimumToAmount, actualToAmount); unchecked { _feeCollected[toAsset] += haircut; } fromAsset.addCash(fromAmount); // haircut is removed from cash to maintain r* = 1. It is distributed during _mintFee() unchecked { toAsset.removeCash(actualToAmount + haircut); } // revert if cov ratio < 1% to avoid precision error if (uint256(toAsset.cash()).wdiv(toAsset.liability()) < WAD / 100) revert WOMBAT_FORBIDDEN(); } /** * @notice Swap fromToken for toToken, ensures deadline and minimumToAmount and sends quoted amount to `to` address * @dev This function assumes tax free token. * @param fromToken The token being inserted into Pool by user for swap * @param toToken The token wanted by user, leaving the Pool * @param fromAmount The amount of from token inserted * @param minimumToAmount The minimum amount that will be accepted by user as result * @param to The user receiving the result of swap * @param deadline The deadline to be respected */ function swap( address fromToken, address toToken, uint256 fromAmount, uint256 minimumToAmount, address to, uint256 deadline ) external override nonReentrant whenNotPaused returns (uint256 actualToAmount, uint256 haircut) { _checkSameAddress(fromToken, toToken); if (fromAmount == 0) revert WOMBAT_ZERO_AMOUNT(); _checkAddress(to); _ensure(deadline); requireAssetNotPaused(fromToken); IAsset fromAsset = _assetOf(fromToken); IAsset toAsset = _assetOf(toToken); uint8 toDecimal = toAsset.underlyingTokenDecimals(); (actualToAmount, haircut) = _swap( fromAsset, toAsset, fromAmount.toWad(fromAsset.underlyingTokenDecimals()), minimumToAmount.toWad(toDecimal) ); actualToAmount = actualToAmount.fromWad(toDecimal); haircut = haircut.fromWad(toDecimal); IERC20(fromToken).safeTransferFrom(msg.sender, address(fromAsset), fromAmount); toAsset.transferUnderlyingToken(to, actualToAmount); emit Swap(msg.sender, fromToken, toToken, fromAmount, actualToAmount, to); } /** * @notice Given an input asset amount and token addresses, calculates the * maximum output token amount (accounting for fees and slippage). * @dev In reverse quote, the haircut is in the `fromAsset` * @param fromToken The initial ERC20 token * @param toToken The token wanted by user * @param fromAmount The given input amount * @return potentialOutcome The potential amount user would receive * @return haircut The haircut that would be applied */ function quotePotentialSwap( address fromToken, address toToken, int256 fromAmount ) public view override returns (uint256 potentialOutcome, uint256 haircut) { _checkSameAddress(fromToken, toToken); if (fromAmount == 0) revert WOMBAT_ZERO_AMOUNT(); IAsset fromAsset = _assetOf(fromToken); IAsset toAsset = _assetOf(toToken); fromAmount = fromAmount.toWad(fromAsset.underlyingTokenDecimals()); (potentialOutcome, haircut) = _quoteFrom(fromAsset, toAsset, fromAmount); potentialOutcome = potentialOutcome.fromWad(toAsset.underlyingTokenDecimals()); if (fromAmount >= 0) { haircut = haircut.fromWad(toAsset.underlyingTokenDecimals()); } else { haircut = haircut.fromWad(fromAsset.underlyingTokenDecimals()); } } /** * @notice Returns the minimum input asset amount required to buy the given output asset amount * (accounting for fees and slippage) * @dev To be used by frontend * @param fromToken The initial ERC20 token * @param toToken The token wanted by user * @param toAmount The given output amount * @return amountIn The input amount required * @return haircut The haircut that would be applied */ function quoteAmountIn( address fromToken, address toToken, int256 toAmount ) external view override returns (uint256 amountIn, uint256 haircut) { return quotePotentialSwap(toToken, fromToken, -toAmount); } /* Queries */ /** * @notice Returns the exchange rate of the LP token * @param token The address of the token * @return xr The exchange rate of LP token */ function exchangeRate(address token) external view returns (uint256 xr) { IAsset asset = _assetOf(token); if (asset.totalSupply() == 0) return WAD; return xr = uint256(asset.liability()).wdiv(uint256(asset.totalSupply())); } function globalEquilCovRatio() external view returns (uint256 equilCovRatio, uint256 invariantInUint) { int256 invariant; int256 SL; (invariant, SL) = _globalInvariantFunc(); equilCovRatio = uint256(_equilCovRatio(invariant, SL, int256(ampFactor))); invariantInUint = uint256(invariant); } function tipBucketBalance(address token) public view returns (uint256 balance) { IAsset asset = _assetOf(token); unchecked { return asset.underlyingTokenBalance().toWad(asset.underlyingTokenDecimals()) - asset.cash() - _feeCollected[asset]; } } /* Utils */ function _globalInvariantFunc() internal view virtual returns (int256 D, int256 SL) { int256 A = int256(ampFactor); for (uint256 i; i < _sizeOfAssetList(); ++i) { IAsset asset = _getAsset(_getKeyAtIndex(i)); // overflow is unrealistic int256 A_i = int256(uint256(asset.cash())); int256 L_i = int256(uint256(asset.liability())); // Assume when L_i == 0, A_i always == 0 if (L_i == 0) { // avoid division of 0 continue; } int256 r_i = A_i.wdiv(L_i); SL += L_i; D += L_i.wmul(r_i - A.wdiv(r_i)); } } function _mintFeeIfNeeded(IAsset asset) internal { uint256 feeCollected = _feeCollected[asset]; if (feeCollected == 0 || feeCollected < mintFeeThreshold) { return; } else { _mintFee(asset); } } /** * @notice Private function to send fee collected to the fee beneficiary * @param asset The address of the asset to collect fee */ function _mintFee(IAsset asset) internal returns (uint256 feeCollected) { feeCollected = _feeCollected[asset]; if (feeCollected == 0) { // early return return 0; } { // dividend to veWOM uint256 dividend = feeCollected.wmul(WAD - lpDividendRatio - retentionRatio); if (dividend > 0) { asset.transferUnderlyingToken(feeTo, dividend.fromWad(asset.underlyingTokenDecimals())); } } { // dividend to LP uint256 lpDividend = feeCollected.wmul(lpDividendRatio); if (lpDividend > 0) { // exact deposit to maintain r* = 1 // increase the value of the LP token, i.e. assetsPerShare (, uint256 liabilityToMint, ) = _exactDepositToInEquil(asset, lpDividend); asset.addLiability(liabilityToMint); asset.addCash(lpDividend); } } // remainings are sent to the tipbucket _feeCollected[asset] = 0; } function _mintAllFees() internal { for (uint256 i; i < _sizeOfAssetList(); ++i) { IAsset asset = _getAsset(_getKeyAtIndex(i)); _mintFee(asset); } } /** * @notice Send fee collected to the fee beneficiary * @param token The address of the token to collect fee */ function mintFee(address token) external returns (uint256 feeCollected) { return _mintFee(_assetOf(token)); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.5; /** * @dev Interface of the MasterWombat */ interface IMasterWombat { function getAssetPid(address asset) external view returns (uint256 pid); function poolLength() external view returns (uint256); function pendingTokens( uint256 _pid, address _user ) external view returns ( uint256 pendingRewards, address bonusTokenAddress, string memory bonusTokenSymbol, uint256 pendingBonusToken ); function rewarderBonusTokenInfo( uint256 _pid ) external view returns (address bonusTokenAddress, string memory bonusTokenSymbol); function massUpdatePools() external; function updatePool(uint256 _pid) external; function deposit(uint256 _pid, uint256 _amount) external returns (uint256, uint256); function multiClaim( uint256[] memory _pids ) external returns (uint256 transfered, uint256[] memory rewards, uint256[] memory additionalRewards); function withdraw(uint256 _pid, uint256 _amount) external returns (uint256, uint256); function emergencyWithdraw(uint256 _pid) external; function migrate(uint256[] calldata _pids) external; function depositFor(uint256 _pid, uint256 _amount, address _user) external; function updateFactor(address _user, uint256 _newVeWomBalance) external; }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 1000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"CORE_UNDERFLOW","type":"error"},{"inputs":[],"name":"WOMBAT_AMOUNT_TOO_LOW","type":"error"},{"inputs":[],"name":"WOMBAT_ASSET_ALREADY_EXIST","type":"error"},{"inputs":[],"name":"WOMBAT_ASSET_ALREADY_PAUSED","type":"error"},{"inputs":[],"name":"WOMBAT_ASSET_NOT_EXISTS","type":"error"},{"inputs":[],"name":"WOMBAT_ASSET_NOT_PAUSED","type":"error"},{"inputs":[],"name":"WOMBAT_CASH_NOT_ENOUGH","type":"error"},{"inputs":[],"name":"WOMBAT_COV_RATIO_LIMIT_EXCEEDED","type":"error"},{"inputs":[],"name":"WOMBAT_EXPIRED","type":"error"},{"inputs":[],"name":"WOMBAT_FORBIDDEN","type":"error"},{"inputs":[],"name":"WOMBAT_INVALID_VALUE","type":"error"},{"inputs":[],"name":"WOMBAT_SAME_ADDRESS","type":"error"},{"inputs":[],"name":"WOMBAT_ZERO_ADDRESS","type":"error"},{"inputs":[],"name":"WOMBAT_ZERO_AMOUNT","type":"error"},{"inputs":[],"name":"WOMBAT_ZERO_LIQUIDITY","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"}],"name":"AssetAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"}],"name":"AssetRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidity","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FillPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","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":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"PausedAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetAmpFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"SetDev","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"lpDividendRatio","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"retentionRatio","type":"uint256"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"SetFeeTo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetHaircutRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"SetMasterWombat","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetMintFeeThreshold","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"fromToken","type":"address"},{"indexed":false,"internalType":"address","name":"toToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"TransferTipBucket","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"UnpausedAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidity","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"asset","type":"address"}],"name":"addAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"addressOfAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ampFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minimumLiquidity","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"shouldStake","type":"bool"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dev","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endCovRatio","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"exchangeRate","outputs":[{"internalType":"uint256","name":"xr","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"fillPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalEquilCovRatio","outputs":[{"internalType":"uint256","name":"equilCovRatio","type":"uint256"},{"internalType":"uint256","name":"invariantInUint","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"haircutRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ampFactor_","type":"uint256"},{"internalType":"uint256","name":"haircutRate_","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpDividendRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"masterWombat","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"mintFee","outputs":[{"internalType":"uint256","name":"feeCollected","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintFeeThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"pauseAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"int256","name":"toAmount","type":"int256"}],"name":"quoteAmountIn","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"haircut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"quotePotentialDeposit","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"int256","name":"fromAmount","type":"int256"}],"name":"quotePotentialSwap","outputs":[{"internalType":"uint256","name":"potentialOutcome","type":"uint256"},{"internalType":"uint256","name":"haircut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"quotePotentialWithdraw","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"quotePotentialWithdrawFromOtherAsset","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"withdrewAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"removeAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"retentionRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ampFactor_","type":"uint256"}],"name":"setAmpFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"startCovRatio_","type":"uint128"},{"internalType":"uint128","name":"endCovRatio_","type":"uint128"}],"name":"setCovRatioFeeParam","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"dev_","type":"address"}],"name":"setDev","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lpDividendRatio_","type":"uint256"},{"internalType":"uint256","name":"retentionRatio_","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeTo_","type":"address"}],"name":"setFeeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"haircutRate_","type":"uint256"}],"name":"setHaircutRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"masterWombat_","type":"address"}],"name":"setMasterWombat","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintFeeThreshold_","type":"uint256"}],"name":"setMintFeeThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startCovRatio","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"minimumToAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swap","outputs":[{"internalType":"uint256","name":"actualToAmount","type":"uint256"},{"internalType":"uint256","name":"haircut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"tipBucketBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"transferTipBucket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"unpauseAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"minimumAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"minimumAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"withdrawFromOtherAsset","outputs":[{"internalType":"uint256","name":"toAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6080806040523461001657614e92908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b600090813560e01c908163017b8292146129eb57508063017e7e58146129c45780630705999d14612951578063070f81d91461275e57806309a5fca3146123525780630f91f06f14611f4f5780633753b14a14611e9e5780633bd61ba814611e775780633f4ba83a14611dda5780634a5e42b114611c785780634a6fee0e14611be45780634fdd64e514611bd057806352f7c98814611b145780635b14f18314611ad75780635c975abb14611ab45780635cfe092e14611a9657806361858e23146119fc5780636281baef146116985780636922d5ca1461167a578063715018a61461161e57806374cbbdb9146115fc578063815bfd29146115de5780638456cb59146115835780638a2dfe091461132e5780638da5cb5b14611307578063907448ed1461127a57806391cca3db1461125357806393aeea02146111005780639908fc8b14610eaf5780639abacb4114610de95780639e4416b714610d8b578063a4275ceb1461096b578063aa6ca808146108a5578063d043c56514610857578063d0dd0e5614610839578063d30ffeda14610815578063d477f05f146107a2578063da4899971461065e578063dc3b7c8b1461063a578063e4a3011614610487578063e9249cc714610457578063f2fde38b146103b0578063f46901ed1461033d578063f57e84d5146102b5578063f8b49e7214610281578063fb7f5cc2146102595763fca8f3081461022657600080fd5b3461025657604061024a61024461023c36612a92565b929092613833565b916147f9565b82519182526020820152f35b80fd5b503461025657806003193601126102565760206001600160801b036101085416604051908152f35b50346102565760203660031901126102565760206102ad6102a86102a3612a07565b613064565b614c89565b604051908152f35b5034610256576020366003190112610256576004356102d2612add565b670de0b6b3a76400008111610313576020817fc6e60196bbe5909add58d5ae8a27082013670bce0855321773c33085af5c32439260cb55604051908152a180f35b60046040517f12b019e1000000000000000000000000000000000000000000000000000000008152fd5b5034610256576020366003190112610256577ff6b59ffc88fbb27f33470b919e00b41139ee340eb349521f0cbbc1504ce29c3e60206001600160a01b03610382612a07565b61038a612add565b61039381612f03565b16806001600160a01b031960d054161760d055604051908152a180f35b5034610256576020366003190112610256576103ca612a07565b6103d2612add565b6001600160a01b038116156103ed576103ea90612b35565b80f35b608460405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b50346102565760203660031901126102565760206001600160a01b0361047e6102a3612a07565b16604051908152f35b50346102565761049636612ac7565b9082549160ff8360081c16159182809361062d575b8015610616575b156105ac5760ff198481166001178655938361059b575b506104e360ff865460081c166104de81612e45565b612e45565b6104ec33612b35565b84549361050c60ff8660081c1661050281612e45565b6001606555612e45565b6097541660975560ca5560cb55670de0b6b3a764000060cd55336001600160a01b031960cf54161760cf55610563575b506101087718fae27693b40000000000000000000014d1120d7b1600008282541617905580f35b61ff00191681557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498602060405160018152a13861053c565b61ffff1916610101178555386104c9565b608460405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b50303b1580156104b25750600160ff8516146104b2565b50600160ff8516106104ab565b50346102565760203660031901126102565760206102ad610659612a07565b6149ba565b503461025657604036600319011261025657610678612a07565b610680612a33565b610688612add565b61069181612f03565b61069a82612f03565b6106bd826001600160a01b0380911660005260d460205260406000205416151590565b610778576001600160a01b03809116911680835260d460205260408320826001600160a01b031982541617905560d35460d5602052806040852055680100000000000000008110156107645761073b61071d82600185940160d355612eb6565b819391549060031b916001600160a01b03809116831b921b19161790565b90557f0bb5715f0f217c2fe9a0c877ea87d474380c641102f3440ee2a4c8b9d97909188380a380f35b602484634e487b7160e01b81526041600452fd5b60046040517f8991c4c4000000000000000000000000000000000000000000000000000000008152fd5b5034610256576020366003190112610256577f8daa6bb00de042a7d51816fcee4a3d5c1e91a2128fd64fa1a9a38afa0d26beb560206001600160a01b036107e7612a07565b6107ef612add565b6107f881612f03565b16806001600160a01b031960cf54161760cf55604051908152a180f35b50346102565760203660031901126102565760206102ad610834612a07565b614b0e565b5034610256578060031936011261025657602060cb54604051908152f35b5034610256576020366003190112610256577f935ce123b6388f8dbf76890f8240a48198f1e0a884f26939aa604a46bb65b7066020600435610897612add565b8060ce55604051908152a180f35b5034610256578060031936011261025657604051809160d354908183526020809301809260d383527f915c3eb987b20e1af620c1403197bf687fb7f18513b3a73fde6e78c7072c41a690835b81811061094e5750505084610907910385612ba7565b60405193838594850191818652518092526040850193925b82811061092e57505050500390f35b83516001600160a01b03168552869550938101939281019260010161091f565b82546001600160a01b0316845292860192600192830192016108f1565b503461025657606036600319011261025657610985612a07565b61098d612a33565b610998604435612dd6565b6109a28183612e07565b6109ab82613064565b91836109b683613064565b6109c260443586613a95565b5050948560405163961be39160e01b81526020816004816001600160a01b0387165afa8015610d25576001600160781b038391610a07938891610ce5575b5016612be8565b9060405163705727b560e01b81526020816004816001600160a01b0388165afa8015610c7657610a508691610a56938991610cab575b506001600160781b036044359116612be8565b94613ec7565b670de0b6b3a764000090818103610d5b575b5050506040519263961be39160e01b84526020846004816001600160a01b0385165afa938415610d25578594610d30575b5060206001600160a01b039160046040518094819363705727b560e01b8352165afa8015610d255788610b1095610b0a95610b0a95610af8956001600160a01b039a95610d04575b506001600160781b038060ca549616931690613d3b565b610b0460cb5482612c64565b90612be8565b93613064565b92169260405163961be39160e01b8152602081600481885afa8015610c76576001600160781b038391610b4a938991610ce5575016612be8565b60405163705727b560e01b8152602081600481895afa8015610cda57610b83918891610cab57506001600160781b036044359116612be8565b610b9681610b918585612c0b565b612c8e565b916001600160801b0361010854168311610c81575b505050604051926020846004816339420b4560e11b988982525afa918215610c7657610bec6020936004926001600160a01b03978a92610c5e575b50612cf7565b9560405195869384928352165afa918215610c535790610c15929160409592610c225750612cf7565b9082519182526020820152f35b610c4591925060203d602011610c4c575b610c3d8183612ba7565b810190612c18565b9038610be6565b503d610c33565b6040513d86823e3d90fd5b610c45919250863d8811610c4c57610c3d8183612ba7565b6040513d88823e3d90fd5b93610c9d8193610c98610b0494610ca29798612c8e565b612d42565b612c64565b90388080610bab565b610ccd915060203d602011610cd3575b610cc58183612ba7565b810190612bc9565b38610a3d565b503d610cbb565b6040513d89823e3d90fd5b610cfe915060203d602011610cd357610cc58183612ba7565b38610a00565b610d1e91955060203d602011610cd357610cc58183612ba7565b9338610ae1565b6040513d87823e3d90fd5b6001600160a01b03919450610d53602091823d8411610cd357610cc58183612ba7565b949150610a99565b90610d8192949950610d7a8282610d73828298612c31565b049b612c31565b0493612c31565b0495388080610a68565b503461025657602036600319011261025657600435610da8612add565b670de0b6b3a76400008111610313576020817f294c562dcfcd9be941287844539aeba4ae641cd1fc9a2883337c6b2e87cbd98a9260ca55604051908152a180f35b503461025657602036600319011261025657610e03612a07565b610e0b612f8d565b6001600160a01b03811680835260c960205260ff60408420541615610e8557825260c960209081526040808420805460ff1916905580516001600160a01b03909316835233918301919091527fdb198487a4c70aa000c3b1020f52688b4b86d3ebcedc84a793e03ed3b480902c9190819081015b0390a180f35b60046040517f855b7373000000000000000000000000000000000000000000000000000000008152fd5b503461025657610ebe36612a49565b610ecc9593919294956130c1565b610ed4612f3d565b610ede8685612e07565b81156110d657610ef690610ef184612f03565b613394565b610eff83612fa1565b610f0883613064565b91610f1286613064565b91604051956339420b4560e11b8088526020886004816001600160a01b0389165afa978815611085576000986110b5575b506040519081526020816004816001600160a01b038a165afa908115611085576001600160a01b03610fae8a610fa8610fa18b610fb9988c610f9b86610f958e9c8b9c600091611096575b508d61302a565b9361302a565b92613f9a565b9d90612cf7565b9b612cf7565b981696163387613116565b6001600160a01b0383163b156110915760006040518094639e79eaa560e01b825281836001600160a01b03826110098d8a60048401602090939291936001600160a01b0360408201951681520152565b0393165af18015611085576040976001600160a01b03948592611076575b5088519586521660208501528684015284606084015216907f54787c404bb33c88e86f4baf88183a3b0141d0a848e6a9f7a13b66ae3a9b73d160803392a3600160655582519182526020820152f35b61107f90612b7d565b89611027565b6040513d6000823e3d90fd5b600080fd5b6110af915060203d602011610c4c57610c3d8183612ba7565b38610f8e565b6110cf91985060203d602011610c4c57610c3d8183612ba7565b9689610f43565b60046040517fb483c10f000000000000000000000000000000000000000000000000000000008152fd5b50346102565760603660031901126102565761111a612a07565b602435906044356001600160a01b03808216918281036110915761113c612add565b61114584613064565b9061114f85614b0e565b86116103135782879216906040516339420b4560e11b8152602081600481865afa8015610c5357611188918591611235575b5088612cf7565b823b1561123157604051639e79eaa560e01b81526001600160a01b0392909216600483015260248201529082908290604490829084905af1801561122657611208575b50927f5f22a22ebe30686f71d42a70ccacf870a335e22ee2ac6d257af1b2551582eda692856060936040519316835260208301526040820152a180f35b6112159094939294612b7d565b61122257909184386111cb565b8480fd5b6040513d84823e3d90fd5b8380fd5b61124d915060203d8111610c4c57610c3d8183612ba7565b38611181565b503461025657806003193601126102565760206001600160a01b0360cf5416604051908152f35b50346102565760403660031901126102565760206001600160a01b0361129e612a07565b6112b86112b1602435926102a384612dd6565b9182613a95565b94919290506004604051809581936339420b4560e11b8352165afa918215610c535790610c159291604095926112ee5750612cf7565b610c4591925060203d8111610c4c57610c3d8183612ba7565b503461025657806003193601126102565760206001600160a01b0360335416604051908152f35b5034610256578060031936011261025657808160ca54908060d354905b81831061138557604085610c158661138061137b8b61137661136c876137d8565b6002830590613780565b613818565b613833565b613e89565b9091936113c561139486612eb6565b91906001600160a01b03928391549060031b1c166001600160a01b0380911660005260d46020526040600020541690565b169560405163961be39160e01b815260209081816004818c5afa908115610d25578591611566575b506040519063705727b560e01b825282826004818d5afa918215610c76578692611547575b506004836001600160781b038094169b604051928380926317d1d06960e11b82525afa938415610cda578794611517575b5050891561150857899161145791166137d8565b918160029384820561146891613780565b9061147291613818565b918291670de0b6b3a7640000958661148b8194846137f5565b6114949061375c565b0561149e91613780565b9b6114a8916137f5565b6114b19061375c565b05926114bc896137d8565b9082056114c891613780565b906114d291613818565b6114db916137bf565b6114e4916137f5565b6114ed9061375c565b056114f791613780565b9361150190614aff565b919061134b565b50505095509361150190614aff565b9080929450813d8311611540575b61152f8183612ba7565b810103126110915751913880611443565b503d611525565b61155f919250833d8511610cd357610cc58183612ba7565b9038611412565b61157d9150823d8411610cd357610cc58183612ba7565b386113ed565b503461025657806003193601126102565761159c612f8d565b6115a4612f3d565b600160ff1960975416176097557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a180f35b5034610256578060031936011261025657602060cd54604051908152f35b503461025657806003193601126102565760206101085460801c604051908152f35b5034610256578060031936011261025657611637612add565b60006001600160a01b036033546001600160a01b03198116603355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b5034610256578060031936011261025657602060cc54604051908152f35b50346102565760c0366003190112610256576116b2612a07565b602435604435916116c1612a1d565b9260a435908115918215036119f8576116d86130c1565b6116e0612f3d565b83156110d6576116ef85612f03565b6116fa608435613394565b61170383612fa1565b61170c83613064565b6001600160a01b039283821690611727878333888a16613116565b156117f6576020600491604051928380926339420b4560e11b82525afa9081156117eb579261178e9261178889937ff5dd9317b9e63ac316ce44acc85f670b54b339cfa3e9076e1dd55065b922314b979660209c916117ce575b508961302a565b906133c6565b945b6117be8660405193849316963396846040919493926001600160a01b03606083019616825260208201520152565b0390a36001606555604051908152f35b6117e591508c3d8111610c4c57610c3d8183612ba7565b38611781565b6040513d8a823e3d90fd5b906118098460d199949895995416612f03565b604051966339420b4560e11b88526020978881600481875afa908115610d25576118479392916118409187916119e157508961302a565b30926133c6565b60d1546040517f095ea7b30000000000000000000000000000000000000000000000000000000081529089166001600160a01b0316600482015260248101829052909690818160448187875af18015610c53576119b4575b508760d1541691604051907faf929a8000000000000000000000000000000000000000000000000000000000825260048201528181602481865afa918215610c53578492611986575b5050813b156119825782916064839260405194859384927f90210d7e00000000000000000000000000000000000000000000000000000000845260048401528b60248401528c891660448401525af180156112265761196e575b50506020947ff5dd9317b9e63ac316ce44acc85f670b54b339cfa3e9076e1dd55065b922314b91611790565b6119788291612b7d565b6102565780611942565b8280fd5b90809250813d83116119ad575b61199d8183612ba7565b81010312611091575138806118e8565b503d611993565b6119d390823d84116119da575b6119cb8183612ba7565b8101906130a9565b503861189f565b503d6119c1565b6117e591508b3d8d11610c4c57610c3d8183612ba7565b8580fd5b503461025657604036600319011261025657611a196102a3612a07565b6040516339420b4560e11b81526020816004816001600160a01b0386165afa908115611a8b57611a609291611a5a9160409591611a6d575b5060243561302a565b9061354e565b8351928352602083015250f35b611a85915060203d8111610c4c57610c3d8183612ba7565b38611a51565b6040513d85823e3d90fd5b5034610256578060031936011261025657602060ce54604051908152f35b5034610256578060031936011261025657602060ff609754166040519015158152f35b50346102565760203660031901126102565760ff60406020926001600160a01b03611b00612a07565b16815260c984522054166040519015158152f35b503461025657611b2336612ac7565b90611b2c612add565b670de0b6b3a76400008183011161031357825b60d354811015611b945780611b896102a86001600160a01b03611b64611b8f95612eb6565b90549060031b1c166001600160a01b0380911660005260d46020526040600020541690565b50614aff565b611b3f565b506040907f032dc6a2d839eb179729a55633fdf1c41a1fc4739394154117005db2b354b9b5928060cc558160cd5582519182526020820152a180f35b503461025657604061024a61024436612a92565b5034610256576040366003190112610256576004356001600160801b038082168092036110915760243590811680820361109157611c20612add565b670de0b6b3a76400008310908115611c6e575b506103135761010891837fffffffffffffffffffffffffffffffff0000000000000000000000000000000084549360801b1692161717905580f35b9050821138611c33565b503461025657602036600319011261025657611c92612a07565b611c9a612add565b611cbd816001600160a01b0380911660005260d460205260406000205416151590565b15611db0576001600160a01b038091169081835260d4602052604083208054906001600160a01b03198216905560d5602052604084205460d3549060001991828101908111611d9c5761071d85611d16611d3b93612eb6565b90549060031b1c169283895260d56020528060408a2055878952886040812055612eb6565b905560d3548015611d885701611d5081612eb6565b8482549160031b1b1916905560d35516907f0fa1e4606af435f32f05b3804033d2933e691fab32ee74d2db6fa82d2741f1ea8380a380f35b602486634e487b7160e01b81526031600452fd5b602487634e487b7160e01b81526011600452fd5b60046040517fecb004d4000000000000000000000000000000000000000000000000000000008152fd5b5034610256578060031936011261025657611df3612f8d565b60975460ff811615611e335760ff19166097557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a180f35b606460405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152fd5b503461025657806003193601126102565760206001600160a01b0360d15416604051908152f35b503461025657602036600319011261025657611eb8612a07565b611ec0612f8d565b611ee3816001600160a01b0380911660005260d460205260406000205416151590565b15611db057610e7f81611f167fdcb65c0553aaa433aadd180404ff195259c48f78aa50f877ebcb4bb215129a4e93612fa1565b6001600160a01b031680845260c960209081526040808620805460ff191660011790558051928352339183019190915290918291820190565b503461025657611fa0611f97611f6436612a49565b611f73979395929691976130c1565b611f7b612f3d565b611f8488612f03565b611f8d86612dd6565b610ef18584612e07565b6102a381612fa1565b92611faa82613064565b906001600160a01b03851694611fc285873381613116565b600095611fce82614c46565b611fd88683613a95565b509097823b1561025657604051632770a7eb60e21b81526001600160a01b038416600482015260248101899052818160448183885af1801561122657612343575b5090823b1561232b576040516313f3df3160e31b8152896004820152828160248183885af18015611a8b5790839161232f575b5050823b1561232b576040519063d8b8785360e01b82526004820152818160248183875af1801561122657908291612317575b505060405163705727b560e01b91828252602082600481875afa91821561230a5781926122e9575b506001600160781b0380921615159384612230575b5050505061221f576001600160a01b03831695604051936339420b4560e11b948581526020816004818c5afa80156110855761210495610f9b926000926121fe575b5061302a565b50604051918252602082600481885afa9081156110855761212c926000926121dd5750612cf7565b92803b1561109157604051639e79eaa560e01b81526001600160a01b038616600482015260248101859052906000908290604490829084905af18015611085576020957ffb80d861da582b723be2d19507ce3e03851820c464abea89156ec77e089b1ad9926001600160a01b03926121ce575b506117be6040519283921695873396846040919493926001600160a01b03606083019616825260208201520152565b6121d790612b7d565b8761219f565b6121f791925060203d602011610c4c57610c3d8183612ba7565b9087610be6565b61221891925060203d602011610c4c57610c3d8183612ba7565b908c6120fe565b60046040516316fde3bd60e21b8152fd5b90919293506040519363961be39160e01b8552602085600481855afa948515611a8b5783956122c7575b5060209060046040518094819382525afa9182156122bb5792662386f26fc1000094928192612292959161229c575b50169116612c8e565b10888080806120bc565b6122b5915060203d602011610cd357610cc58183612ba7565b8d612289565b604051903d90823e3d90fd5b60209195506122e290823d8411610cd357610cc58183612ba7565b949061225a565b61230391925060203d602011610cd357610cc58183612ba7565b908b6120a7565b50604051903d90823e3d90fd5b61232090612b7d565b61025657808a61207f565b5080fd5b61233890612b7d565b61232b57818b61204c565b61234c90612b7d565b8a612019565b50346102565760a03660031901126102565761236c612a07565b612374612a1d565b61237c6130c1565b612384612f3d565b61238f602435612dd6565b61239881612f03565b6123a3608435613394565b6123ac82613064565b916123c46024356001600160a01b0385163381613116565b6040516339420b4560e11b81526020816004816001600160a01b0388165afa908115610d2557859161273f575b506123fe8160443561302a565b9061240885614c46565b61242161241760243587613a95565b509190809461351d565b6001600160a01b0386163b1561109157604051632770a7eb60e21b81526001600160a01b03871660048201526024803590820152600090818180604481010381836001600160a01b038d165af1801561122657612730575b50906001600160a01b0387163b1561232b576040516313f3df3160e31b81528460048201528281602481836001600160a01b038d165af18015611a8b5790839161271c575b50506001600160a01b0387163b1561232b576040519063d8b8785360e01b825260048201528181602481836001600160a01b038c165af1801561122657908291612708575b505060405163705727b560e01b908181526020816004816001600160a01b038c165afa908115611a8b57906001600160781b039184916126e9575b50161515918261262b575b505061221f5761255891612cf7565b926001600160a01b0381163b1561122257846040518092639e79eaa560e01b825281836001600160a01b03826125a88b8b60048401602090939291936001600160a01b0360408201951681520152565b0393165af18015610d2557612617575b50602093507ffb80d861da582b723be2d19507ce3e03851820c464abea89156ec77e089b1ad96001600160a01b03604051931692806117be33948760243591846040919493926001600160a01b03606083019616825260208201520152565b6126218591612b7d565b61123157836125b8565b60405163961be39160e01b81529250906020836004816001600160a01b038c165afa9283156112265782936126c8575b506040519081526020816004816001600160a01b038c165afa9182156122bb576001600160781b036126a193662386f26fc1000095938293916126a95750169116612c8e565b103880612549565b6126c2915060203d602011610cd357610cc58183612ba7565b38612289565b6126e291935060203d602011610cd357610cc58183612ba7565b913861265b565b612702915060203d602011610cd357610cc58183612ba7565b3861253e565b61271190612b7d565b610256578038612503565b61272590612b7d565b61232b5781386124be565b61273990612b7d565b38612479565b612758915060203d602011610c4c57610c3d8183612ba7565b386123f1565b503461025657604036600319011261025657612778612a07565b90602435612784612f8d565b6001600160a01b0361279584613064565b16604051632672469960e21b81526020908181600481865afa908115610d25578591612924575b50604051906339420b4560e11b82528282600481875afa918215610c7657906127ec92918792612905575061302a565b60405163961be39160e01b81528281600481875afa908115610c765761283b93926001600160781b0360d29361282b938a916128e8575b501690612be8565b9184875252604085205490612be8565b821161031357803b156119825782809160246040518094819362b64f3d60e51b83528760048401525af18015611a8b576128b7575b50604080516001600160a01b039094168452602084019190915290917f4941e18a2bcbb0f9fa0081238f26793a8ad8c202b913ae8bf5f7e523f68ff1379181908101610e7f565b916128e27f4941e18a2bcbb0f9fa0081238f26793a8ad8c202b913ae8bf5f7e523f68ff13793612b7d565b91612870565b6128ff9150863d8811610cd357610cc58183612ba7565b38612823565b61291d919250843d8611610c4c57610c3d8183612ba7565b90386120fe565b90508181813d831161294a575b61293b8183612ba7565b810103126112225751386127bc565b503d612931565b5034610256576020366003190112610256577ffeb9010869b6ccec4557ddbbce947afeace5efc66cdff52c5e533c09336a8f2d60206001600160a01b03612996612a07565b61299e612add565b6129a781612f03565b16806001600160a01b031960d154161760d155604051908152a180f35b503461025657806003193601126102565760206001600160a01b0360d05416604051908152f35b90503461232b578160031936011261232b5760209060ca548152f35b600435906001600160a01b038216820361109157565b606435906001600160a01b038216820361109157565b602435906001600160a01b038216820361109157565b60c0906003190112611091576001600160a01b03600435818116810361109157916024358281168103611091579160443591606435916084359081168103611091579060a43590565b6060906003190112611091576001600160a01b0390600435828116810361109157916024359081168103611091579060443590565b6040906003190112611091576004359060243590565b6001600160a01b03603354163303612af157565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b603354906001600160a01b0380911691826001600160a01b0319821617603355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3565b67ffffffffffffffff8111612b9157604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff821117612b9157604052565b9081602091031261109157516001600160781b03811681036110915790565b91908203918211612bf557565b634e487b7160e01b600052601160045260246000fd5b91908201809211612bf557565b90816020910312611091575160ff811681036110915790565b81810292918115918404141715612bf557565b8115612c4e570490565b634e487b7160e01b600052601260045260246000fd5b90612c6e91612c31565b6706f05b59d3b200008101809111612bf557670de0b6b3a7640000900490565b90670de0b6b3a764000091828102928184041490151715612bf557612cba612cbf928260011c90612c0b565b612c44565b90565b60ff6011199116019060ff8211612bf557565b60ff166012039060ff8211612bf557565b60ff16604d8111612bf557600a0a90565b9060ff81166012811015612d21575090612d1b612d16612cbf93612cd5565b612ce6565b90612c44565b601210612d2c575090565b90612d3c612d16612cbf93612cc2565b90612c31565b610108548060801c91828411600014612d66576004604051630c2b153f60e31b8152fd5b6001600160801b0382168411801590612dcc575b612dc357612cbf936001600160801b0392831691612dac9190838111612db8576000905b848303920391800203612c44565b60011c92031690612c8e565b838103800290612d9e565b50505050600090565b5080841115612d7a565b15612ddd57565b60046040517f3f3835e7000000000000000000000000000000000000000000000000000000008152fd5b6001600160a01b03908116911614612e1b57565b60046040517feeb7911f000000000000000000000000000000000000000000000000000000008152fd5b15612e4c57565b608460405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152fd5b60d354811015612eed5760d36000527f915c3eb987b20e1af620c1403197bf687fb7f18513b3a73fde6e78c7072c41a60190600090565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b031615612f1357565b60046040517f0bd3e3ed000000000000000000000000000000000000000000000000000000008152fd5b60ff60975416612f4957565b606460405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152fd5b6001600160a01b0360cf5416330361221f57565b6001600160a01b031660005260c960205260ff60406000205416612fc157565b60046040517f8654830a000000000000000000000000000000000000000000000000000000008152fd5b60ff811660128110156130055750612d16612cbf91612cd5565b6012106130125750600190565b612d1661301e91612cc2565b8015612c4e5760010490565b9060ff81166012811015613049575090612d3c612d16612cbf93612cd5565b601210613054575090565b90612d1b612d16612cbf93612cc2565b613087816001600160a01b0380911660005260d460205260406000205416151590565b15611db0576001600160a01b0380911660005260d46020526040600020541690565b90816020910312611091575180151581036110915790565b6002606554146130d2576002606555565b606460405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b93929190604051602093848201927f23b872dd0000000000000000000000000000000000000000000000000000000084526001600160a01b039485809216602485015216604483015260648201526064815260a081019267ffffffffffffffff9682851088861117612b9157169060e0810184811088821117612b91576040528484527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460c0820152600080938192519082855af1903d156132b4573d9687116132a057613205949596604051906131f788601f19601f8401160183612ba7565b81528093873d92013e6132c1565b805190828215928315613288575b5050501561321e5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b61329893508201810191016130a9565b388281613213565b602483634e487b7160e01b81526041600452fd5b9150613205939495506060915b9192901561332257508151156132d5575090565b3b156132de5790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156133355750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401526000935b82851061337b575050604492506000838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350613358565b421161339c57565b60046040517f549b6335000000000000000000000000000000000000000000000000000000008152fd5b6001600160a01b03919294936133db82614c46565b6133e5848361354e565b509390926133fd8480996133f882612dd6565b61351d565b1690813b1561109157604093845162b64f3d60e51b81526000966004820152868160248183885af1801561351357613500575b50823b156119f85784519063283c3d8160e21b82526004820152858160248183875af180156134f6579086916134e2575b5050813b156112225783517f40c10f190000000000000000000000000000000000000000000000000000000081526001600160a01b03919091166004820152602481019290925290919083908390604490829084905af19081156134d957506134c8575050565b6134d28291612b7d565b6102565750565b513d84823e3d90fd5b6134eb90612b7d565b611222578438613461565b85513d88823e3d90fd5b61350c90969196612b7d565b9438613430565b86513d89823e3d90fd5b1161352457565b60046040517ff512a720000000000000000000000000000000000000000000000000000000008152fd5b6000916001600160a01b036000921660409081519363961be39160e01b85526020938486600481865afa9586156137035782966136e4575b5083519063705727b560e01b968783528683600481885afa9283156136da5784936136b3575b506135cc6135d19160ca54906001600160781b0380809716911685613844565b61370d565b978189106136aa57508703955b84519081528581600481875afa9081156136a0578391613683575b5016938461360a5750505050508192565b806004938551948580926318160ddd60e01b82525afa93841561367857508193613643575b50505090612cba6136409285612c31565b92565b9091809350813d8311613671575b61365b8183612ba7565b81010312610256575051612cba6136403861362f565b503d613651565b51913d9150823e3d90fd5b61369a9150863d8811610cd357610cc58183612ba7565b386135f9565b85513d85823e3d90fd5b969750966135de565b6135d19193506136d26135cc91893d8b11610cd357610cc58183612ba7565b9391506135ac565b86513d86823e3d90fd5b6136fc919650853d8711610cd357610cc58183612ba7565b9438613586565b84513d84823e3d90fd5b600081126137185790565b606460405162461bcd60e51b815260206004820152601660248201527f76616c7565206d75737420626520706f736974697665000000000000000000006044820152fd5b906706f05b59d3b200009160008382019384129112908015821691151617612bf557565b91909160008382019384129112908015821691151617612bf557565b90670de0b6b3a764000060008382039312818412811691841390151617612bf557565b81810392916000138015828513169184121617612bf557565b90670de0b6b3a764000091828102928184051490151715612bf557565b818102929160008212600160ff1b821416612bf5578184051490151715612bf557565b8115612c4e57600160ff1b8114600019831416612bf5570590565b600160ff1b8114612bf55760000390565b919081156139df5760006138588483613780565b126139b557613877816138718461137661136c846137d8565b94613780565b670de0b6b3a7640000918261389c6138976138918961379c565b856137f5565b61375c565b0590836138ac613897878a6137f5565b05908160011b916002830503612bf5576138c98593928493613780565b966138d4878a6137f5565b906138de91613818565b6138e7916137bf565b6138f190846137f5565b6138fa9061375c565b059180613906916137f5565b61390f9061375c565b05613919916137bf565b918161392582876137f5565b61392e9061375c565b0590613939916137f5565b6139429061375c565b0561394c91613780565b9161395782806137f5565b928160021b90600482058303612bf5576139a36139b19461399d61398d6002986139876139a896611376986137f5565b906137bf565b9161399781613833565b926139e6565b90613780565b6137d8565b84830590613780565b0590565b60046040517fae032660000000000000000000000000000000000000000000000000000000008152fd5b5050905090565b919060006003841315613a8657506000811380613a7c575b15613a4557915b600280613a1b85613a168186613818565b613780565b05915b848303613a2a57505050565b9193509081613a3d85613a168185613818565b059190613a1e565b6000811280613a6a575b15613a6357613a5d90613833565b91613a05565b5081613a05565b5082613a7582613833565b1315613a4f565b50828113156139fe565b929050613a8f57565b60019150565b916000926001600160a01b036000911690604080519063705727b560e01b938483526020968784600481855afa938415613d31578594613d0e575b50613ae5906001600160781b03809516612c31565b82516318160ddd60e01b81528881600481865afa908115613d04578691613cd5575b50613b1191612c44565b96613b1b88612dd6565b613b2488613833565b94835163961be39160e01b81528281600481875afa908115613ccb57839187918491613cae575b501697600486518096819382525afa938415613ca4575092613c87575b5050169060ca5490613b7a8184613780565b9083613b85866137d8565b60028205613b9291613780565b90613b9c91613818565b8094613ba7856137d8565b90670de0b6b3a764000096808894936002869505613bc491613780565b90613bce91613818565b613bd7916137bf565b613be0916137f5565b613be99061375c565b0591613bf48561379c565b613bfd916137f5565b613c069061375c565b05613c1091613780565b60029005928392613c2184806137f5565b9280613c2c916137f5565b613c35916137f5565b613c3e9061375c565b05613c4891613780565b90613c52916139e6565b613c5b91613780565b613c64916137bf565b613c6d9061370d565b92838310613c805750612cbf8383612be8565b9092508192565b613c9d9250803d10610cd357610cc58183612ba7565b3880613b68565b51903d90823e3d90fd5b613cc59150833d8511610cd357610cc58183612ba7565b38613b4b565b85513d84823e3d90fd5b90508881813d8311613cfd575b613cec8183612ba7565b810103126119f85751613b11613b07565b503d613ce2565b84513d88823e3d90fd5b613ae5919450613d2a90893d8b11610cd357610cc58183612ba7565b9390613ad0565b83513d87823e3d90fd5b9390949180158015613e81575b6139b55783818192613d5a8989613780565b9780613d6683806137f5565b90613d7091613818565b98670de0b6b3a7640000809a8c613d8788806137f5565b90613d9191613818565b613d9a91613780565b613da4908a6137f5565b613dad9061375c565b05613db7916137bf565b95613dc191613780565b613dca906137d8565b60028205613dd791613780565b90613de191613818565b80613deb876137d8565b60028205613df891613780565b90613e0291613818565b613e0b916137bf565b613e14916137f5565b90613e1e91613818565b91613e28906137d8565b60028205613e3591613780565b90613e3f91613818565b613e48916137bf565b90613e5291613e89565b613e5b916137f5565b613e649061375c565b0590613e6f916137bf565b6000811215612cbf57612cbf90613833565b508315613d48565b613e9381806137f5565b908260021b9280840560041490151715612bf557613ec281613ebd6139b19461399d6002976137d8565b6139e6565b6137bf565b604051916317d1d06960e11b9081845260209182856004816001600160a01b038098165afa94851561108557600095613f6a575b50906004839260405195869384928352165afa90811561108557600091613f3e575b509050670de0b6b3a76400009180830292830403612bf557612cbf91612c44565b82813d8311613f63575b613f528183612ba7565b810103126102565750518038613f1d565b503d613f48565b90948382813d8311613f93575b613f818183612ba7565b81010312610256575051936004613efb565b503d613f77565b90929391613fa9818584614145565b90948195613fb881809961351d565b6000926001600160a01b038093169485855260209360d285526040978887208481540190551690813b156119f8578591602483928a51948593849262b64f3d60e51b845260048401525af1801561413b57614128575b50843b15611231578551916313f3df3160e31b8352016004820152828160248183885af180156136a057908391614114575b505083519263961be39160e01b84528184600481845afa9384156136a05783946140ee575b50908060049286519384809263705727b560e01b82525afa9283156140e357916140ae9391662386f26fc100009593926140c6575b50506001600160781b038091169116612c8e565b106140b65750565b600490516316fde3bd60e21b8152fd5b6140dc9250803d10610cd357610cc58183612ba7565b388061409a565b8551903d90823e3d90fd5b829194509161410b600493823d8411610cd357610cc58183612ba7565b94919250614065565b61411d90612b7d565b61232b578138614040565b61413490949194612b7d565b923861400e565b87513d87823e3d90fd5b91929092614154818585614521565b80949180966000851215600014614270575050506001600160a01b0316906040519063961be39160e01b82526020908183600481875afa801561424e57600493600091614253575b50826001600160781b03809216956040519586809263705727b560e01b82525afa801561424e57610b91946141df94600092614231575b50501692839185612c0b565b916142026141f6610108546001600160801b031690565b6001600160801b031690565b831161420d57505050565b94610c9d61422c93610c986142259484979899612c8e565b8092612be8565b920190565b6142479250803d10610cd357610cc58183612ba7565b38806141d3565b611085565b61426a9150833d8511610cd357610cc58183612ba7565b3861419c565b8093949297506001600160a01b03919596501660405163961be39160e01b815260208082600481865afa91821561424e57600092614379575b506004816001600160781b03809416946040519283809263705727b560e01b82525afa91821561424e5760009261435c575b5050166142ec81610b918885612c0b565b6101085492906001600160801b0384161061430b575050505050509190565b909193955061431c92949750612c8e565b9060801c111561434b5761433261433991613833565b8483614398565b9061434691938491614145565b905090565b6004604051630c2b153f60e31b8152fd5b6143729250803d10610cd357610cc58183612ba7565b38806142db565b816143919293503d8411610cd357610cc58183612ba7565b90386142a9565b6040516339420b4560e11b81529193926020926001600160a01b038316908481600481855afa90811561424e57600091614504575b506143d781612feb565b946040519263705727b560e01b84528184600481845afa801561424e576004946000916144e7575b50826144246144146141f66101085460801c90565b6001600160781b03809416612c64565b926040519687809263961be39160e01b82525afa801561424e5761445c95614457946000926144ca575b50501690612be8565b612cf7565b91600195868361447661446f8888612c31565b8486614145565b501061434b579291905b8484106144965750505050612cbf929350612c31565b90919293808501881c9088856144af8985028688614145565b50106144c1575050935b929190614480565b909550016144b9565b6144e09250803d10610cd357610cc58183612ba7565b388061444e565b6144fe9150833d8511610cd357610cc58183612ba7565b386143ff565b61451b9150853d8711610c4c57610c3d8183612ba7565b386143cd565b919060008083126147d7575b6040805163961be39160e01b8082526001600160a01b038781169760209660049691949192888389818e5afa9283156147cd5784936147ae575b506001600160781b03809316908b88518b818c8163705727b560e01b958682525afa9081156147a4579086918891614787575b50169184169389518881528c818d81895afa90811561477d576145cb929188918f8b92614760575b50501699613ec7565b670de0b6b3a764000090818103614730575b50508a908a8a518096819382525afa92831561472657918b9161461094938794614707575b50888660ca54951692613d3b565b988289139788958b90876146fd575b50508515614689575b50505050506146635750501561464b575061464861422560cb5483612c64565b91565b919061465a612cbf9193613833565b60cb5490612c64565b517f9a6114b7000000000000000000000000000000000000000000000000000000008152fd5b90919280949550891294856146a8575b50505050503880808080614628565b8192939495508787518095819382525afa9384156140e357936146de575b50506146d186613833565b9116103880808080614699565b6146f5929350803d10610cd357610cc58183612ba7565b9038806146c6565b1095508a3861461f565b61471f9194508c8d3d10610cd357610cc58183612ba7565b9238614602565b88513d87823e3d90fd5b80828e949f959682826147496147579661475094612c31565b0498612c31565b04946137f5565b059b90386145dd565b6147769250803d10610cd357610cc58183612ba7565b388f6145c2565b8b513d8a823e3d90fd5b61479e91508d803d10610cd357610cc58183612ba7565b3861459a565b8a513d89823e3d90fd5b6147c6919350893d8b11610cd357610cc58183612ba7565b9138614567565b87513d86823e3d90fd5b916147f39061137661136c6147ed60cb5461379c565b926137d8565b9161452d565b929091926148078382612e07565b83156110d65761481961481f91613064565b92613064565b916001600160a01b03938482166040958651926339420b4560e11b908185526020958686600481885afa958615614969578996614870936148689260009261494a575b50614974565b988991614145565b9092819516938951938385528785600481895afa94851561493f5788956000926148a09284926149275750612cf7565b99126148fb575050600488518094819382525afa9283156148f057612cbf949596506000936148d1575b5050612cf7565b6148e8929350803d10610c4c57610c3d8183612ba7565b9038806148ca565b86513d6000823e3d90fd5b93509350600488518094819382525afa9283156148f057612cbf949596506000936148d1575050612cf7565b610c45919250883d8a11610c4c57610c3d8183612ba7565b8b513d6000823e3d90fd5b6149629192508a3d8c11610c4c57610c3d8183612ba7565b9038614862565b8a513d6000823e3d90fd5b9060ff81166012811015614999575090614993612d16612cbf93612cd5565b906137f5565b6012106149a4575090565b906149b4612d16612cbf93612cc2565b90613818565b6149cb6001600160a01b0391613064565b1660408051916318160ddd60e01b928381526020908181600481865afa908115614ab757600091614ad2575b5015614ac25782519363705727b560e01b85528185600481865afa948515614ab757600095614a96575b508190600485518095819382525afa928315614a8c5750600092614a54575b50506001600160781b03612cbf9216612c8e565b81819392933d8311614a85575b614a6b8183612ba7565b810103126102565750516001600160781b03612cbf614a40565b503d614a61565b513d6000823e3d90fd5b82919550614ab090823d8411610cd357610cc58183612ba7565b9490614a21565b84513d6000823e3d90fd5b50505050670de0b6b3a764000090565b908282813d8311614af8575b614ae88183612ba7565b81010312610256575051386149f7565b503d614ade565b6000198114612bf55760010190565b614b1f6001600160a01b0391613064565b166040805190632672469960e21b82526020918281600481875afa908115614c3b57600091614c0e575b508151906339420b4560e11b82528382600481885afa918215614beb5790614b799291600092614bf6575061302a565b9181519363961be39160e01b85528185600481845afa948515614beb57600095614bbc575b506001600160781b03929160d2916000525260002054921690030390565b60d291955091614be16001600160781b039493823d8411610cd357610cc58183612ba7565b9591509192614b9e565b83513d6000823e3d90fd5b61291d919250853d8711610c4c57610c3d8183612ba7565b908382813d8311614c34575b614c248183612ba7565b8101031261025657505138614b49565b503d614c1a565b82513d6000823e3d90fd5b6001600160a01b03811660005260d26020526040600020548015908115614c7d575b5015614c715750565b614c7a90614c89565b50565b905060ce541138614c68565b9060006001600160a01b039283811680835260d260205260409182842054958615614e535760cd54670de0b6b3a7640000908103908111614e3f57614cd4614cda9160cc5490612be8565b88612c64565b9081614db1575b5050614cef60cd5487612c64565b9081614d04575b5050825260d2602052812055565b81614d0e9161354e565b509050823b156112225783519063283c3d8160e21b82526004820152848160248183875af18015614da757908591614d93575b5050813b156112315782519062b64f3d60e51b82526004820152838160248183865af18015614d865790849115614cf657614d7b90612b7d565b611982578238614cf6565b50505051903d90823e3d90fd5b614d9c90612b7d565b611231578338614d41565b84513d87823e3d90fd5b60d05416908451906339420b4560e11b8252602082600481885afa9182156135135790614de5929188926112ee5750612cf7565b90833b156119f8578451639e79eaa560e01b81526001600160a01b039190911660048201526024810191909152848160448183875af18015614da757614e2c575b80614ce1565b614e3890949194612b7d565b9238614e26565b602486634e487b7160e01b81526011600452fd5b5092945050505056fea264697066735822122089da207b08b1fed51b5d9d793a7a6258e967bbfa337a2db97ee6319e3beb454264736f6c63430008120033
Deployed Bytecode
0x608080604052600436101561001357600080fd5b600090813560e01c908163017b8292146129eb57508063017e7e58146129c45780630705999d14612951578063070f81d91461275e57806309a5fca3146123525780630f91f06f14611f4f5780633753b14a14611e9e5780633bd61ba814611e775780633f4ba83a14611dda5780634a5e42b114611c785780634a6fee0e14611be45780634fdd64e514611bd057806352f7c98814611b145780635b14f18314611ad75780635c975abb14611ab45780635cfe092e14611a9657806361858e23146119fc5780636281baef146116985780636922d5ca1461167a578063715018a61461161e57806374cbbdb9146115fc578063815bfd29146115de5780638456cb59146115835780638a2dfe091461132e5780638da5cb5b14611307578063907448ed1461127a57806391cca3db1461125357806393aeea02146111005780639908fc8b14610eaf5780639abacb4114610de95780639e4416b714610d8b578063a4275ceb1461096b578063aa6ca808146108a5578063d043c56514610857578063d0dd0e5614610839578063d30ffeda14610815578063d477f05f146107a2578063da4899971461065e578063dc3b7c8b1461063a578063e4a3011614610487578063e9249cc714610457578063f2fde38b146103b0578063f46901ed1461033d578063f57e84d5146102b5578063f8b49e7214610281578063fb7f5cc2146102595763fca8f3081461022657600080fd5b3461025657604061024a61024461023c36612a92565b929092613833565b916147f9565b82519182526020820152f35b80fd5b503461025657806003193601126102565760206001600160801b036101085416604051908152f35b50346102565760203660031901126102565760206102ad6102a86102a3612a07565b613064565b614c89565b604051908152f35b5034610256576020366003190112610256576004356102d2612add565b670de0b6b3a76400008111610313576020817fc6e60196bbe5909add58d5ae8a27082013670bce0855321773c33085af5c32439260cb55604051908152a180f35b60046040517f12b019e1000000000000000000000000000000000000000000000000000000008152fd5b5034610256576020366003190112610256577ff6b59ffc88fbb27f33470b919e00b41139ee340eb349521f0cbbc1504ce29c3e60206001600160a01b03610382612a07565b61038a612add565b61039381612f03565b16806001600160a01b031960d054161760d055604051908152a180f35b5034610256576020366003190112610256576103ca612a07565b6103d2612add565b6001600160a01b038116156103ed576103ea90612b35565b80f35b608460405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b50346102565760203660031901126102565760206001600160a01b0361047e6102a3612a07565b16604051908152f35b50346102565761049636612ac7565b9082549160ff8360081c16159182809361062d575b8015610616575b156105ac5760ff198481166001178655938361059b575b506104e360ff865460081c166104de81612e45565b612e45565b6104ec33612b35565b84549361050c60ff8660081c1661050281612e45565b6001606555612e45565b6097541660975560ca5560cb55670de0b6b3a764000060cd55336001600160a01b031960cf54161760cf55610563575b506101087718fae27693b40000000000000000000014d1120d7b1600008282541617905580f35b61ff00191681557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498602060405160018152a13861053c565b61ffff1916610101178555386104c9565b608460405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b50303b1580156104b25750600160ff8516146104b2565b50600160ff8516106104ab565b50346102565760203660031901126102565760206102ad610659612a07565b6149ba565b503461025657604036600319011261025657610678612a07565b610680612a33565b610688612add565b61069181612f03565b61069a82612f03565b6106bd826001600160a01b0380911660005260d460205260406000205416151590565b610778576001600160a01b03809116911680835260d460205260408320826001600160a01b031982541617905560d35460d5602052806040852055680100000000000000008110156107645761073b61071d82600185940160d355612eb6565b819391549060031b916001600160a01b03809116831b921b19161790565b90557f0bb5715f0f217c2fe9a0c877ea87d474380c641102f3440ee2a4c8b9d97909188380a380f35b602484634e487b7160e01b81526041600452fd5b60046040517f8991c4c4000000000000000000000000000000000000000000000000000000008152fd5b5034610256576020366003190112610256577f8daa6bb00de042a7d51816fcee4a3d5c1e91a2128fd64fa1a9a38afa0d26beb560206001600160a01b036107e7612a07565b6107ef612add565b6107f881612f03565b16806001600160a01b031960cf54161760cf55604051908152a180f35b50346102565760203660031901126102565760206102ad610834612a07565b614b0e565b5034610256578060031936011261025657602060cb54604051908152f35b5034610256576020366003190112610256577f935ce123b6388f8dbf76890f8240a48198f1e0a884f26939aa604a46bb65b7066020600435610897612add565b8060ce55604051908152a180f35b5034610256578060031936011261025657604051809160d354908183526020809301809260d383527f915c3eb987b20e1af620c1403197bf687fb7f18513b3a73fde6e78c7072c41a690835b81811061094e5750505084610907910385612ba7565b60405193838594850191818652518092526040850193925b82811061092e57505050500390f35b83516001600160a01b03168552869550938101939281019260010161091f565b82546001600160a01b0316845292860192600192830192016108f1565b503461025657606036600319011261025657610985612a07565b61098d612a33565b610998604435612dd6565b6109a28183612e07565b6109ab82613064565b91836109b683613064565b6109c260443586613a95565b5050948560405163961be39160e01b81526020816004816001600160a01b0387165afa8015610d25576001600160781b038391610a07938891610ce5575b5016612be8565b9060405163705727b560e01b81526020816004816001600160a01b0388165afa8015610c7657610a508691610a56938991610cab575b506001600160781b036044359116612be8565b94613ec7565b670de0b6b3a764000090818103610d5b575b5050506040519263961be39160e01b84526020846004816001600160a01b0385165afa938415610d25578594610d30575b5060206001600160a01b039160046040518094819363705727b560e01b8352165afa8015610d255788610b1095610b0a95610b0a95610af8956001600160a01b039a95610d04575b506001600160781b038060ca549616931690613d3b565b610b0460cb5482612c64565b90612be8565b93613064565b92169260405163961be39160e01b8152602081600481885afa8015610c76576001600160781b038391610b4a938991610ce5575016612be8565b60405163705727b560e01b8152602081600481895afa8015610cda57610b83918891610cab57506001600160781b036044359116612be8565b610b9681610b918585612c0b565b612c8e565b916001600160801b0361010854168311610c81575b505050604051926020846004816339420b4560e11b988982525afa918215610c7657610bec6020936004926001600160a01b03978a92610c5e575b50612cf7565b9560405195869384928352165afa918215610c535790610c15929160409592610c225750612cf7565b9082519182526020820152f35b610c4591925060203d602011610c4c575b610c3d8183612ba7565b810190612c18565b9038610be6565b503d610c33565b6040513d86823e3d90fd5b610c45919250863d8811610c4c57610c3d8183612ba7565b6040513d88823e3d90fd5b93610c9d8193610c98610b0494610ca29798612c8e565b612d42565b612c64565b90388080610bab565b610ccd915060203d602011610cd3575b610cc58183612ba7565b810190612bc9565b38610a3d565b503d610cbb565b6040513d89823e3d90fd5b610cfe915060203d602011610cd357610cc58183612ba7565b38610a00565b610d1e91955060203d602011610cd357610cc58183612ba7565b9338610ae1565b6040513d87823e3d90fd5b6001600160a01b03919450610d53602091823d8411610cd357610cc58183612ba7565b949150610a99565b90610d8192949950610d7a8282610d73828298612c31565b049b612c31565b0493612c31565b0495388080610a68565b503461025657602036600319011261025657600435610da8612add565b670de0b6b3a76400008111610313576020817f294c562dcfcd9be941287844539aeba4ae641cd1fc9a2883337c6b2e87cbd98a9260ca55604051908152a180f35b503461025657602036600319011261025657610e03612a07565b610e0b612f8d565b6001600160a01b03811680835260c960205260ff60408420541615610e8557825260c960209081526040808420805460ff1916905580516001600160a01b03909316835233918301919091527fdb198487a4c70aa000c3b1020f52688b4b86d3ebcedc84a793e03ed3b480902c9190819081015b0390a180f35b60046040517f855b7373000000000000000000000000000000000000000000000000000000008152fd5b503461025657610ebe36612a49565b610ecc9593919294956130c1565b610ed4612f3d565b610ede8685612e07565b81156110d657610ef690610ef184612f03565b613394565b610eff83612fa1565b610f0883613064565b91610f1286613064565b91604051956339420b4560e11b8088526020886004816001600160a01b0389165afa978815611085576000986110b5575b506040519081526020816004816001600160a01b038a165afa908115611085576001600160a01b03610fae8a610fa8610fa18b610fb9988c610f9b86610f958e9c8b9c600091611096575b508d61302a565b9361302a565b92613f9a565b9d90612cf7565b9b612cf7565b981696163387613116565b6001600160a01b0383163b156110915760006040518094639e79eaa560e01b825281836001600160a01b03826110098d8a60048401602090939291936001600160a01b0360408201951681520152565b0393165af18015611085576040976001600160a01b03948592611076575b5088519586521660208501528684015284606084015216907f54787c404bb33c88e86f4baf88183a3b0141d0a848e6a9f7a13b66ae3a9b73d160803392a3600160655582519182526020820152f35b61107f90612b7d565b89611027565b6040513d6000823e3d90fd5b600080fd5b6110af915060203d602011610c4c57610c3d8183612ba7565b38610f8e565b6110cf91985060203d602011610c4c57610c3d8183612ba7565b9689610f43565b60046040517fb483c10f000000000000000000000000000000000000000000000000000000008152fd5b50346102565760603660031901126102565761111a612a07565b602435906044356001600160a01b03808216918281036110915761113c612add565b61114584613064565b9061114f85614b0e565b86116103135782879216906040516339420b4560e11b8152602081600481865afa8015610c5357611188918591611235575b5088612cf7565b823b1561123157604051639e79eaa560e01b81526001600160a01b0392909216600483015260248201529082908290604490829084905af1801561122657611208575b50927f5f22a22ebe30686f71d42a70ccacf870a335e22ee2ac6d257af1b2551582eda692856060936040519316835260208301526040820152a180f35b6112159094939294612b7d565b61122257909184386111cb565b8480fd5b6040513d84823e3d90fd5b8380fd5b61124d915060203d8111610c4c57610c3d8183612ba7565b38611181565b503461025657806003193601126102565760206001600160a01b0360cf5416604051908152f35b50346102565760403660031901126102565760206001600160a01b0361129e612a07565b6112b86112b1602435926102a384612dd6565b9182613a95565b94919290506004604051809581936339420b4560e11b8352165afa918215610c535790610c159291604095926112ee5750612cf7565b610c4591925060203d8111610c4c57610c3d8183612ba7565b503461025657806003193601126102565760206001600160a01b0360335416604051908152f35b5034610256578060031936011261025657808160ca54908060d354905b81831061138557604085610c158661138061137b8b61137661136c876137d8565b6002830590613780565b613818565b613833565b613e89565b9091936113c561139486612eb6565b91906001600160a01b03928391549060031b1c166001600160a01b0380911660005260d46020526040600020541690565b169560405163961be39160e01b815260209081816004818c5afa908115610d25578591611566575b506040519063705727b560e01b825282826004818d5afa918215610c76578692611547575b506004836001600160781b038094169b604051928380926317d1d06960e11b82525afa938415610cda578794611517575b5050891561150857899161145791166137d8565b918160029384820561146891613780565b9061147291613818565b918291670de0b6b3a7640000958661148b8194846137f5565b6114949061375c565b0561149e91613780565b9b6114a8916137f5565b6114b19061375c565b05926114bc896137d8565b9082056114c891613780565b906114d291613818565b6114db916137bf565b6114e4916137f5565b6114ed9061375c565b056114f791613780565b9361150190614aff565b919061134b565b50505095509361150190614aff565b9080929450813d8311611540575b61152f8183612ba7565b810103126110915751913880611443565b503d611525565b61155f919250833d8511610cd357610cc58183612ba7565b9038611412565b61157d9150823d8411610cd357610cc58183612ba7565b386113ed565b503461025657806003193601126102565761159c612f8d565b6115a4612f3d565b600160ff1960975416176097557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a180f35b5034610256578060031936011261025657602060cd54604051908152f35b503461025657806003193601126102565760206101085460801c604051908152f35b5034610256578060031936011261025657611637612add565b60006001600160a01b036033546001600160a01b03198116603355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b5034610256578060031936011261025657602060cc54604051908152f35b50346102565760c0366003190112610256576116b2612a07565b602435604435916116c1612a1d565b9260a435908115918215036119f8576116d86130c1565b6116e0612f3d565b83156110d6576116ef85612f03565b6116fa608435613394565b61170383612fa1565b61170c83613064565b6001600160a01b039283821690611727878333888a16613116565b156117f6576020600491604051928380926339420b4560e11b82525afa9081156117eb579261178e9261178889937ff5dd9317b9e63ac316ce44acc85f670b54b339cfa3e9076e1dd55065b922314b979660209c916117ce575b508961302a565b906133c6565b945b6117be8660405193849316963396846040919493926001600160a01b03606083019616825260208201520152565b0390a36001606555604051908152f35b6117e591508c3d8111610c4c57610c3d8183612ba7565b38611781565b6040513d8a823e3d90fd5b906118098460d199949895995416612f03565b604051966339420b4560e11b88526020978881600481875afa908115610d25576118479392916118409187916119e157508961302a565b30926133c6565b60d1546040517f095ea7b30000000000000000000000000000000000000000000000000000000081529089166001600160a01b0316600482015260248101829052909690818160448187875af18015610c53576119b4575b508760d1541691604051907faf929a8000000000000000000000000000000000000000000000000000000000825260048201528181602481865afa918215610c53578492611986575b5050813b156119825782916064839260405194859384927f90210d7e00000000000000000000000000000000000000000000000000000000845260048401528b60248401528c891660448401525af180156112265761196e575b50506020947ff5dd9317b9e63ac316ce44acc85f670b54b339cfa3e9076e1dd55065b922314b91611790565b6119788291612b7d565b6102565780611942565b8280fd5b90809250813d83116119ad575b61199d8183612ba7565b81010312611091575138806118e8565b503d611993565b6119d390823d84116119da575b6119cb8183612ba7565b8101906130a9565b503861189f565b503d6119c1565b6117e591508b3d8d11610c4c57610c3d8183612ba7565b8580fd5b503461025657604036600319011261025657611a196102a3612a07565b6040516339420b4560e11b81526020816004816001600160a01b0386165afa908115611a8b57611a609291611a5a9160409591611a6d575b5060243561302a565b9061354e565b8351928352602083015250f35b611a85915060203d8111610c4c57610c3d8183612ba7565b38611a51565b6040513d85823e3d90fd5b5034610256578060031936011261025657602060ce54604051908152f35b5034610256578060031936011261025657602060ff609754166040519015158152f35b50346102565760203660031901126102565760ff60406020926001600160a01b03611b00612a07565b16815260c984522054166040519015158152f35b503461025657611b2336612ac7565b90611b2c612add565b670de0b6b3a76400008183011161031357825b60d354811015611b945780611b896102a86001600160a01b03611b64611b8f95612eb6565b90549060031b1c166001600160a01b0380911660005260d46020526040600020541690565b50614aff565b611b3f565b506040907f032dc6a2d839eb179729a55633fdf1c41a1fc4739394154117005db2b354b9b5928060cc558160cd5582519182526020820152a180f35b503461025657604061024a61024436612a92565b5034610256576040366003190112610256576004356001600160801b038082168092036110915760243590811680820361109157611c20612add565b670de0b6b3a76400008310908115611c6e575b506103135761010891837fffffffffffffffffffffffffffffffff0000000000000000000000000000000084549360801b1692161717905580f35b9050821138611c33565b503461025657602036600319011261025657611c92612a07565b611c9a612add565b611cbd816001600160a01b0380911660005260d460205260406000205416151590565b15611db0576001600160a01b038091169081835260d4602052604083208054906001600160a01b03198216905560d5602052604084205460d3549060001991828101908111611d9c5761071d85611d16611d3b93612eb6565b90549060031b1c169283895260d56020528060408a2055878952886040812055612eb6565b905560d3548015611d885701611d5081612eb6565b8482549160031b1b1916905560d35516907f0fa1e4606af435f32f05b3804033d2933e691fab32ee74d2db6fa82d2741f1ea8380a380f35b602486634e487b7160e01b81526031600452fd5b602487634e487b7160e01b81526011600452fd5b60046040517fecb004d4000000000000000000000000000000000000000000000000000000008152fd5b5034610256578060031936011261025657611df3612f8d565b60975460ff811615611e335760ff19166097557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a180f35b606460405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152fd5b503461025657806003193601126102565760206001600160a01b0360d15416604051908152f35b503461025657602036600319011261025657611eb8612a07565b611ec0612f8d565b611ee3816001600160a01b0380911660005260d460205260406000205416151590565b15611db057610e7f81611f167fdcb65c0553aaa433aadd180404ff195259c48f78aa50f877ebcb4bb215129a4e93612fa1565b6001600160a01b031680845260c960209081526040808620805460ff191660011790558051928352339183019190915290918291820190565b503461025657611fa0611f97611f6436612a49565b611f73979395929691976130c1565b611f7b612f3d565b611f8488612f03565b611f8d86612dd6565b610ef18584612e07565b6102a381612fa1565b92611faa82613064565b906001600160a01b03851694611fc285873381613116565b600095611fce82614c46565b611fd88683613a95565b509097823b1561025657604051632770a7eb60e21b81526001600160a01b038416600482015260248101899052818160448183885af1801561122657612343575b5090823b1561232b576040516313f3df3160e31b8152896004820152828160248183885af18015611a8b5790839161232f575b5050823b1561232b576040519063d8b8785360e01b82526004820152818160248183875af1801561122657908291612317575b505060405163705727b560e01b91828252602082600481875afa91821561230a5781926122e9575b506001600160781b0380921615159384612230575b5050505061221f576001600160a01b03831695604051936339420b4560e11b948581526020816004818c5afa80156110855761210495610f9b926000926121fe575b5061302a565b50604051918252602082600481885afa9081156110855761212c926000926121dd5750612cf7565b92803b1561109157604051639e79eaa560e01b81526001600160a01b038616600482015260248101859052906000908290604490829084905af18015611085576020957ffb80d861da582b723be2d19507ce3e03851820c464abea89156ec77e089b1ad9926001600160a01b03926121ce575b506117be6040519283921695873396846040919493926001600160a01b03606083019616825260208201520152565b6121d790612b7d565b8761219f565b6121f791925060203d602011610c4c57610c3d8183612ba7565b9087610be6565b61221891925060203d602011610c4c57610c3d8183612ba7565b908c6120fe565b60046040516316fde3bd60e21b8152fd5b90919293506040519363961be39160e01b8552602085600481855afa948515611a8b5783956122c7575b5060209060046040518094819382525afa9182156122bb5792662386f26fc1000094928192612292959161229c575b50169116612c8e565b10888080806120bc565b6122b5915060203d602011610cd357610cc58183612ba7565b8d612289565b604051903d90823e3d90fd5b60209195506122e290823d8411610cd357610cc58183612ba7565b949061225a565b61230391925060203d602011610cd357610cc58183612ba7565b908b6120a7565b50604051903d90823e3d90fd5b61232090612b7d565b61025657808a61207f565b5080fd5b61233890612b7d565b61232b57818b61204c565b61234c90612b7d565b8a612019565b50346102565760a03660031901126102565761236c612a07565b612374612a1d565b61237c6130c1565b612384612f3d565b61238f602435612dd6565b61239881612f03565b6123a3608435613394565b6123ac82613064565b916123c46024356001600160a01b0385163381613116565b6040516339420b4560e11b81526020816004816001600160a01b0388165afa908115610d2557859161273f575b506123fe8160443561302a565b9061240885614c46565b61242161241760243587613a95565b509190809461351d565b6001600160a01b0386163b1561109157604051632770a7eb60e21b81526001600160a01b03871660048201526024803590820152600090818180604481010381836001600160a01b038d165af1801561122657612730575b50906001600160a01b0387163b1561232b576040516313f3df3160e31b81528460048201528281602481836001600160a01b038d165af18015611a8b5790839161271c575b50506001600160a01b0387163b1561232b576040519063d8b8785360e01b825260048201528181602481836001600160a01b038c165af1801561122657908291612708575b505060405163705727b560e01b908181526020816004816001600160a01b038c165afa908115611a8b57906001600160781b039184916126e9575b50161515918261262b575b505061221f5761255891612cf7565b926001600160a01b0381163b1561122257846040518092639e79eaa560e01b825281836001600160a01b03826125a88b8b60048401602090939291936001600160a01b0360408201951681520152565b0393165af18015610d2557612617575b50602093507ffb80d861da582b723be2d19507ce3e03851820c464abea89156ec77e089b1ad96001600160a01b03604051931692806117be33948760243591846040919493926001600160a01b03606083019616825260208201520152565b6126218591612b7d565b61123157836125b8565b60405163961be39160e01b81529250906020836004816001600160a01b038c165afa9283156112265782936126c8575b506040519081526020816004816001600160a01b038c165afa9182156122bb576001600160781b036126a193662386f26fc1000095938293916126a95750169116612c8e565b103880612549565b6126c2915060203d602011610cd357610cc58183612ba7565b38612289565b6126e291935060203d602011610cd357610cc58183612ba7565b913861265b565b612702915060203d602011610cd357610cc58183612ba7565b3861253e565b61271190612b7d565b610256578038612503565b61272590612b7d565b61232b5781386124be565b61273990612b7d565b38612479565b612758915060203d602011610c4c57610c3d8183612ba7565b386123f1565b503461025657604036600319011261025657612778612a07565b90602435612784612f8d565b6001600160a01b0361279584613064565b16604051632672469960e21b81526020908181600481865afa908115610d25578591612924575b50604051906339420b4560e11b82528282600481875afa918215610c7657906127ec92918792612905575061302a565b60405163961be39160e01b81528281600481875afa908115610c765761283b93926001600160781b0360d29361282b938a916128e8575b501690612be8565b9184875252604085205490612be8565b821161031357803b156119825782809160246040518094819362b64f3d60e51b83528760048401525af18015611a8b576128b7575b50604080516001600160a01b039094168452602084019190915290917f4941e18a2bcbb0f9fa0081238f26793a8ad8c202b913ae8bf5f7e523f68ff1379181908101610e7f565b916128e27f4941e18a2bcbb0f9fa0081238f26793a8ad8c202b913ae8bf5f7e523f68ff13793612b7d565b91612870565b6128ff9150863d8811610cd357610cc58183612ba7565b38612823565b61291d919250843d8611610c4c57610c3d8183612ba7565b90386120fe565b90508181813d831161294a575b61293b8183612ba7565b810103126112225751386127bc565b503d612931565b5034610256576020366003190112610256577ffeb9010869b6ccec4557ddbbce947afeace5efc66cdff52c5e533c09336a8f2d60206001600160a01b03612996612a07565b61299e612add565b6129a781612f03565b16806001600160a01b031960d154161760d155604051908152a180f35b503461025657806003193601126102565760206001600160a01b0360d05416604051908152f35b90503461232b578160031936011261232b5760209060ca548152f35b600435906001600160a01b038216820361109157565b606435906001600160a01b038216820361109157565b602435906001600160a01b038216820361109157565b60c0906003190112611091576001600160a01b03600435818116810361109157916024358281168103611091579160443591606435916084359081168103611091579060a43590565b6060906003190112611091576001600160a01b0390600435828116810361109157916024359081168103611091579060443590565b6040906003190112611091576004359060243590565b6001600160a01b03603354163303612af157565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b603354906001600160a01b0380911691826001600160a01b0319821617603355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3565b67ffffffffffffffff8111612b9157604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff821117612b9157604052565b9081602091031261109157516001600160781b03811681036110915790565b91908203918211612bf557565b634e487b7160e01b600052601160045260246000fd5b91908201809211612bf557565b90816020910312611091575160ff811681036110915790565b81810292918115918404141715612bf557565b8115612c4e570490565b634e487b7160e01b600052601260045260246000fd5b90612c6e91612c31565b6706f05b59d3b200008101809111612bf557670de0b6b3a7640000900490565b90670de0b6b3a764000091828102928184041490151715612bf557612cba612cbf928260011c90612c0b565b612c44565b90565b60ff6011199116019060ff8211612bf557565b60ff166012039060ff8211612bf557565b60ff16604d8111612bf557600a0a90565b9060ff81166012811015612d21575090612d1b612d16612cbf93612cd5565b612ce6565b90612c44565b601210612d2c575090565b90612d3c612d16612cbf93612cc2565b90612c31565b610108548060801c91828411600014612d66576004604051630c2b153f60e31b8152fd5b6001600160801b0382168411801590612dcc575b612dc357612cbf936001600160801b0392831691612dac9190838111612db8576000905b848303920391800203612c44565b60011c92031690612c8e565b838103800290612d9e565b50505050600090565b5080841115612d7a565b15612ddd57565b60046040517f3f3835e7000000000000000000000000000000000000000000000000000000008152fd5b6001600160a01b03908116911614612e1b57565b60046040517feeb7911f000000000000000000000000000000000000000000000000000000008152fd5b15612e4c57565b608460405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152fd5b60d354811015612eed5760d36000527f915c3eb987b20e1af620c1403197bf687fb7f18513b3a73fde6e78c7072c41a60190600090565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b031615612f1357565b60046040517f0bd3e3ed000000000000000000000000000000000000000000000000000000008152fd5b60ff60975416612f4957565b606460405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152fd5b6001600160a01b0360cf5416330361221f57565b6001600160a01b031660005260c960205260ff60406000205416612fc157565b60046040517f8654830a000000000000000000000000000000000000000000000000000000008152fd5b60ff811660128110156130055750612d16612cbf91612cd5565b6012106130125750600190565b612d1661301e91612cc2565b8015612c4e5760010490565b9060ff81166012811015613049575090612d3c612d16612cbf93612cd5565b601210613054575090565b90612d1b612d16612cbf93612cc2565b613087816001600160a01b0380911660005260d460205260406000205416151590565b15611db0576001600160a01b0380911660005260d46020526040600020541690565b90816020910312611091575180151581036110915790565b6002606554146130d2576002606555565b606460405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b93929190604051602093848201927f23b872dd0000000000000000000000000000000000000000000000000000000084526001600160a01b039485809216602485015216604483015260648201526064815260a081019267ffffffffffffffff9682851088861117612b9157169060e0810184811088821117612b91576040528484527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460c0820152600080938192519082855af1903d156132b4573d9687116132a057613205949596604051906131f788601f19601f8401160183612ba7565b81528093873d92013e6132c1565b805190828215928315613288575b5050501561321e5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b61329893508201810191016130a9565b388281613213565b602483634e487b7160e01b81526041600452fd5b9150613205939495506060915b9192901561332257508151156132d5575090565b3b156132de5790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156133355750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401526000935b82851061337b575050604492506000838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350613358565b421161339c57565b60046040517f549b6335000000000000000000000000000000000000000000000000000000008152fd5b6001600160a01b03919294936133db82614c46565b6133e5848361354e565b509390926133fd8480996133f882612dd6565b61351d565b1690813b1561109157604093845162b64f3d60e51b81526000966004820152868160248183885af1801561351357613500575b50823b156119f85784519063283c3d8160e21b82526004820152858160248183875af180156134f6579086916134e2575b5050813b156112225783517f40c10f190000000000000000000000000000000000000000000000000000000081526001600160a01b03919091166004820152602481019290925290919083908390604490829084905af19081156134d957506134c8575050565b6134d28291612b7d565b6102565750565b513d84823e3d90fd5b6134eb90612b7d565b611222578438613461565b85513d88823e3d90fd5b61350c90969196612b7d565b9438613430565b86513d89823e3d90fd5b1161352457565b60046040517ff512a720000000000000000000000000000000000000000000000000000000008152fd5b6000916001600160a01b036000921660409081519363961be39160e01b85526020938486600481865afa9586156137035782966136e4575b5083519063705727b560e01b968783528683600481885afa9283156136da5784936136b3575b506135cc6135d19160ca54906001600160781b0380809716911685613844565b61370d565b978189106136aa57508703955b84519081528581600481875afa9081156136a0578391613683575b5016938461360a5750505050508192565b806004938551948580926318160ddd60e01b82525afa93841561367857508193613643575b50505090612cba6136409285612c31565b92565b9091809350813d8311613671575b61365b8183612ba7565b81010312610256575051612cba6136403861362f565b503d613651565b51913d9150823e3d90fd5b61369a9150863d8811610cd357610cc58183612ba7565b386135f9565b85513d85823e3d90fd5b969750966135de565b6135d19193506136d26135cc91893d8b11610cd357610cc58183612ba7565b9391506135ac565b86513d86823e3d90fd5b6136fc919650853d8711610cd357610cc58183612ba7565b9438613586565b84513d84823e3d90fd5b600081126137185790565b606460405162461bcd60e51b815260206004820152601660248201527f76616c7565206d75737420626520706f736974697665000000000000000000006044820152fd5b906706f05b59d3b200009160008382019384129112908015821691151617612bf557565b91909160008382019384129112908015821691151617612bf557565b90670de0b6b3a764000060008382039312818412811691841390151617612bf557565b81810392916000138015828513169184121617612bf557565b90670de0b6b3a764000091828102928184051490151715612bf557565b818102929160008212600160ff1b821416612bf5578184051490151715612bf557565b8115612c4e57600160ff1b8114600019831416612bf5570590565b600160ff1b8114612bf55760000390565b919081156139df5760006138588483613780565b126139b557613877816138718461137661136c846137d8565b94613780565b670de0b6b3a7640000918261389c6138976138918961379c565b856137f5565b61375c565b0590836138ac613897878a6137f5565b05908160011b916002830503612bf5576138c98593928493613780565b966138d4878a6137f5565b906138de91613818565b6138e7916137bf565b6138f190846137f5565b6138fa9061375c565b059180613906916137f5565b61390f9061375c565b05613919916137bf565b918161392582876137f5565b61392e9061375c565b0590613939916137f5565b6139429061375c565b0561394c91613780565b9161395782806137f5565b928160021b90600482058303612bf5576139a36139b19461399d61398d6002986139876139a896611376986137f5565b906137bf565b9161399781613833565b926139e6565b90613780565b6137d8565b84830590613780565b0590565b60046040517fae032660000000000000000000000000000000000000000000000000000000008152fd5b5050905090565b919060006003841315613a8657506000811380613a7c575b15613a4557915b600280613a1b85613a168186613818565b613780565b05915b848303613a2a57505050565b9193509081613a3d85613a168185613818565b059190613a1e565b6000811280613a6a575b15613a6357613a5d90613833565b91613a05565b5081613a05565b5082613a7582613833565b1315613a4f565b50828113156139fe565b929050613a8f57565b60019150565b916000926001600160a01b036000911690604080519063705727b560e01b938483526020968784600481855afa938415613d31578594613d0e575b50613ae5906001600160781b03809516612c31565b82516318160ddd60e01b81528881600481865afa908115613d04578691613cd5575b50613b1191612c44565b96613b1b88612dd6565b613b2488613833565b94835163961be39160e01b81528281600481875afa908115613ccb57839187918491613cae575b501697600486518096819382525afa938415613ca4575092613c87575b5050169060ca5490613b7a8184613780565b9083613b85866137d8565b60028205613b9291613780565b90613b9c91613818565b8094613ba7856137d8565b90670de0b6b3a764000096808894936002869505613bc491613780565b90613bce91613818565b613bd7916137bf565b613be0916137f5565b613be99061375c565b0591613bf48561379c565b613bfd916137f5565b613c069061375c565b05613c1091613780565b60029005928392613c2184806137f5565b9280613c2c916137f5565b613c35916137f5565b613c3e9061375c565b05613c4891613780565b90613c52916139e6565b613c5b91613780565b613c64916137bf565b613c6d9061370d565b92838310613c805750612cbf8383612be8565b9092508192565b613c9d9250803d10610cd357610cc58183612ba7565b3880613b68565b51903d90823e3d90fd5b613cc59150833d8511610cd357610cc58183612ba7565b38613b4b565b85513d84823e3d90fd5b90508881813d8311613cfd575b613cec8183612ba7565b810103126119f85751613b11613b07565b503d613ce2565b84513d88823e3d90fd5b613ae5919450613d2a90893d8b11610cd357610cc58183612ba7565b9390613ad0565b83513d87823e3d90fd5b9390949180158015613e81575b6139b55783818192613d5a8989613780565b9780613d6683806137f5565b90613d7091613818565b98670de0b6b3a7640000809a8c613d8788806137f5565b90613d9191613818565b613d9a91613780565b613da4908a6137f5565b613dad9061375c565b05613db7916137bf565b95613dc191613780565b613dca906137d8565b60028205613dd791613780565b90613de191613818565b80613deb876137d8565b60028205613df891613780565b90613e0291613818565b613e0b916137bf565b613e14916137f5565b90613e1e91613818565b91613e28906137d8565b60028205613e3591613780565b90613e3f91613818565b613e48916137bf565b90613e5291613e89565b613e5b916137f5565b613e649061375c565b0590613e6f916137bf565b6000811215612cbf57612cbf90613833565b508315613d48565b613e9381806137f5565b908260021b9280840560041490151715612bf557613ec281613ebd6139b19461399d6002976137d8565b6139e6565b6137bf565b604051916317d1d06960e11b9081845260209182856004816001600160a01b038098165afa94851561108557600095613f6a575b50906004839260405195869384928352165afa90811561108557600091613f3e575b509050670de0b6b3a76400009180830292830403612bf557612cbf91612c44565b82813d8311613f63575b613f528183612ba7565b810103126102565750518038613f1d565b503d613f48565b90948382813d8311613f93575b613f818183612ba7565b81010312610256575051936004613efb565b503d613f77565b90929391613fa9818584614145565b90948195613fb881809961351d565b6000926001600160a01b038093169485855260209360d285526040978887208481540190551690813b156119f8578591602483928a51948593849262b64f3d60e51b845260048401525af1801561413b57614128575b50843b15611231578551916313f3df3160e31b8352016004820152828160248183885af180156136a057908391614114575b505083519263961be39160e01b84528184600481845afa9384156136a05783946140ee575b50908060049286519384809263705727b560e01b82525afa9283156140e357916140ae9391662386f26fc100009593926140c6575b50506001600160781b038091169116612c8e565b106140b65750565b600490516316fde3bd60e21b8152fd5b6140dc9250803d10610cd357610cc58183612ba7565b388061409a565b8551903d90823e3d90fd5b829194509161410b600493823d8411610cd357610cc58183612ba7565b94919250614065565b61411d90612b7d565b61232b578138614040565b61413490949194612b7d565b923861400e565b87513d87823e3d90fd5b91929092614154818585614521565b80949180966000851215600014614270575050506001600160a01b0316906040519063961be39160e01b82526020908183600481875afa801561424e57600493600091614253575b50826001600160781b03809216956040519586809263705727b560e01b82525afa801561424e57610b91946141df94600092614231575b50501692839185612c0b565b916142026141f6610108546001600160801b031690565b6001600160801b031690565b831161420d57505050565b94610c9d61422c93610c986142259484979899612c8e565b8092612be8565b920190565b6142479250803d10610cd357610cc58183612ba7565b38806141d3565b611085565b61426a9150833d8511610cd357610cc58183612ba7565b3861419c565b8093949297506001600160a01b03919596501660405163961be39160e01b815260208082600481865afa91821561424e57600092614379575b506004816001600160781b03809416946040519283809263705727b560e01b82525afa91821561424e5760009261435c575b5050166142ec81610b918885612c0b565b6101085492906001600160801b0384161061430b575050505050509190565b909193955061431c92949750612c8e565b9060801c111561434b5761433261433991613833565b8483614398565b9061434691938491614145565b905090565b6004604051630c2b153f60e31b8152fd5b6143729250803d10610cd357610cc58183612ba7565b38806142db565b816143919293503d8411610cd357610cc58183612ba7565b90386142a9565b6040516339420b4560e11b81529193926020926001600160a01b038316908481600481855afa90811561424e57600091614504575b506143d781612feb565b946040519263705727b560e01b84528184600481845afa801561424e576004946000916144e7575b50826144246144146141f66101085460801c90565b6001600160781b03809416612c64565b926040519687809263961be39160e01b82525afa801561424e5761445c95614457946000926144ca575b50501690612be8565b612cf7565b91600195868361447661446f8888612c31565b8486614145565b501061434b579291905b8484106144965750505050612cbf929350612c31565b90919293808501881c9088856144af8985028688614145565b50106144c1575050935b929190614480565b909550016144b9565b6144e09250803d10610cd357610cc58183612ba7565b388061444e565b6144fe9150833d8511610cd357610cc58183612ba7565b386143ff565b61451b9150853d8711610c4c57610c3d8183612ba7565b386143cd565b919060008083126147d7575b6040805163961be39160e01b8082526001600160a01b038781169760209660049691949192888389818e5afa9283156147cd5784936147ae575b506001600160781b03809316908b88518b818c8163705727b560e01b958682525afa9081156147a4579086918891614787575b50169184169389518881528c818d81895afa90811561477d576145cb929188918f8b92614760575b50501699613ec7565b670de0b6b3a764000090818103614730575b50508a908a8a518096819382525afa92831561472657918b9161461094938794614707575b50888660ca54951692613d3b565b988289139788958b90876146fd575b50508515614689575b50505050506146635750501561464b575061464861422560cb5483612c64565b91565b919061465a612cbf9193613833565b60cb5490612c64565b517f9a6114b7000000000000000000000000000000000000000000000000000000008152fd5b90919280949550891294856146a8575b50505050503880808080614628565b8192939495508787518095819382525afa9384156140e357936146de575b50506146d186613833565b9116103880808080614699565b6146f5929350803d10610cd357610cc58183612ba7565b9038806146c6565b1095508a3861461f565b61471f9194508c8d3d10610cd357610cc58183612ba7565b9238614602565b88513d87823e3d90fd5b80828e949f959682826147496147579661475094612c31565b0498612c31565b04946137f5565b059b90386145dd565b6147769250803d10610cd357610cc58183612ba7565b388f6145c2565b8b513d8a823e3d90fd5b61479e91508d803d10610cd357610cc58183612ba7565b3861459a565b8a513d89823e3d90fd5b6147c6919350893d8b11610cd357610cc58183612ba7565b9138614567565b87513d86823e3d90fd5b916147f39061137661136c6147ed60cb5461379c565b926137d8565b9161452d565b929091926148078382612e07565b83156110d65761481961481f91613064565b92613064565b916001600160a01b03938482166040958651926339420b4560e11b908185526020958686600481885afa958615614969578996614870936148689260009261494a575b50614974565b988991614145565b9092819516938951938385528785600481895afa94851561493f5788956000926148a09284926149275750612cf7565b99126148fb575050600488518094819382525afa9283156148f057612cbf949596506000936148d1575b5050612cf7565b6148e8929350803d10610c4c57610c3d8183612ba7565b9038806148ca565b86513d6000823e3d90fd5b93509350600488518094819382525afa9283156148f057612cbf949596506000936148d1575050612cf7565b610c45919250883d8a11610c4c57610c3d8183612ba7565b8b513d6000823e3d90fd5b6149629192508a3d8c11610c4c57610c3d8183612ba7565b9038614862565b8a513d6000823e3d90fd5b9060ff81166012811015614999575090614993612d16612cbf93612cd5565b906137f5565b6012106149a4575090565b906149b4612d16612cbf93612cc2565b90613818565b6149cb6001600160a01b0391613064565b1660408051916318160ddd60e01b928381526020908181600481865afa908115614ab757600091614ad2575b5015614ac25782519363705727b560e01b85528185600481865afa948515614ab757600095614a96575b508190600485518095819382525afa928315614a8c5750600092614a54575b50506001600160781b03612cbf9216612c8e565b81819392933d8311614a85575b614a6b8183612ba7565b810103126102565750516001600160781b03612cbf614a40565b503d614a61565b513d6000823e3d90fd5b82919550614ab090823d8411610cd357610cc58183612ba7565b9490614a21565b84513d6000823e3d90fd5b50505050670de0b6b3a764000090565b908282813d8311614af8575b614ae88183612ba7565b81010312610256575051386149f7565b503d614ade565b6000198114612bf55760010190565b614b1f6001600160a01b0391613064565b166040805190632672469960e21b82526020918281600481875afa908115614c3b57600091614c0e575b508151906339420b4560e11b82528382600481885afa918215614beb5790614b799291600092614bf6575061302a565b9181519363961be39160e01b85528185600481845afa948515614beb57600095614bbc575b506001600160781b03929160d2916000525260002054921690030390565b60d291955091614be16001600160781b039493823d8411610cd357610cc58183612ba7565b9591509192614b9e565b83513d6000823e3d90fd5b61291d919250853d8711610c4c57610c3d8183612ba7565b908382813d8311614c34575b614c248183612ba7565b8101031261025657505138614b49565b503d614c1a565b82513d6000823e3d90fd5b6001600160a01b03811660005260d26020526040600020548015908115614c7d575b5015614c715750565b614c7a90614c89565b50565b905060ce541138614c68565b9060006001600160a01b039283811680835260d260205260409182842054958615614e535760cd54670de0b6b3a7640000908103908111614e3f57614cd4614cda9160cc5490612be8565b88612c64565b9081614db1575b5050614cef60cd5487612c64565b9081614d04575b5050825260d2602052812055565b81614d0e9161354e565b509050823b156112225783519063283c3d8160e21b82526004820152848160248183875af18015614da757908591614d93575b5050813b156112315782519062b64f3d60e51b82526004820152838160248183865af18015614d865790849115614cf657614d7b90612b7d565b611982578238614cf6565b50505051903d90823e3d90fd5b614d9c90612b7d565b611231578338614d41565b84513d87823e3d90fd5b60d05416908451906339420b4560e11b8252602082600481885afa9182156135135790614de5929188926112ee5750612cf7565b90833b156119f8578451639e79eaa560e01b81526001600160a01b039190911660048201526024810191909152848160448183875af18015614da757614e2c575b80614ce1565b614e3890949194612b7d565b9238614e26565b602486634e487b7160e01b81526011600452fd5b5092945050505056fea264697066735822122089da207b08b1fed51b5d9d793a7a6258e967bbfa337a2db97ee6319e3beb454264736f6c63430008120033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.