Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
PhantomSingleSidedBalancer
Compiler Version
v0.8.16+commit.07a7930e
Contract Source Code (Solidity)
/**
*Submitted for verification at Etherscan.io on 2022-08-30
*/
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.12;
pragma experimental ABIEncoderV2;
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// OpenZeppelin Contracts v4.4.1 (utils/Context.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 Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
uint256 currentAllowance = _allowances[_msgSender()][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(_msgSender(), spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
_afterTokenTransfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason 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 {
// 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
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
/**
* @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;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
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");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
struct StrategyParams {
uint256 performanceFee;
uint256 activation;
uint256 debtRatio;
uint256 minDebtPerHarvest;
uint256 maxDebtPerHarvest;
uint256 lastReport;
uint256 totalDebt;
uint256 totalGain;
uint256 totalLoss;
}
interface VaultAPI is IERC20 {
function name() external view returns (string calldata);
function symbol() external view returns (string calldata);
function decimals() external view returns (uint256);
function apiVersion() external pure returns (string memory);
function permit(
address owner,
address spender,
uint256 amount,
uint256 expiry,
bytes calldata signature
) external returns (bool);
// NOTE: Vyper produces multiple signatures for a given function with "default" args
function deposit() external returns (uint256);
function deposit(uint256 amount) external returns (uint256);
function deposit(uint256 amount, address recipient) external returns (uint256);
// NOTE: Vyper produces multiple signatures for a given function with "default" args
function withdraw() external returns (uint256);
function withdraw(uint256 maxShares) external returns (uint256);
function withdraw(uint256 maxShares, address recipient) external returns (uint256);
function token() external view returns (address);
function strategies(address _strategy) external view returns (StrategyParams memory);
function pricePerShare() external view returns (uint256);
function totalAssets() external view returns (uint256);
function depositLimit() external view returns (uint256);
function maxAvailableShares() external view returns (uint256);
/**
* View how much the Vault would increase this Strategy's borrow limit,
* based on its present performance (since its last report). Can be used to
* determine expectedReturn in your Strategy.
*/
function creditAvailable() external view returns (uint256);
/**
* View how much the Vault would like to pull back from the Strategy,
* based on its present performance (since its last report). Can be used to
* determine expectedReturn in your Strategy.
*/
function debtOutstanding() external view returns (uint256);
/**
* View how much the Vault expect this Strategy to return at the current
* block, based on its present performance (since its last report). Can be
* used to determine expectedReturn in your Strategy.
*/
function expectedReturn() external view returns (uint256);
/**
* This is the main contact point where the Strategy interacts with the
* Vault. It is critical that this call is handled as intended by the
* Strategy. Therefore, this function will be called by BaseStrategy to
* make sure the integration is correct.
*/
function report(
uint256 _gain,
uint256 _loss,
uint256 _debtPayment
) external returns (uint256);
/**
* This function should only be used in the scenario where the Strategy is
* being retired but no migration of the positions are possible, or in the
* extreme scenario that the Strategy needs to be put into "Emergency Exit"
* mode in order for it to exit as quickly as possible. The latter scenario
* could be for any reason that is considered "critical" that the Strategy
* exits its position as fast as possible, such as a sudden change in
* market conditions leading to losses, or an imminent failure in an
* external dependency.
*/
function revokeStrategy() external;
/**
* View the governance address of the Vault to assert privileged functions
* can only be called by governance. The Strategy serves the Vault, so it
* is subject to governance defined by the Vault.
*/
function governance() external view returns (address);
/**
* View the management address of the Vault to assert privileged functions
* can only be called by management. The Strategy serves the Vault, so it
* is subject to management defined by the Vault.
*/
function management() external view returns (address);
/**
* View the guardian address of the Vault to assert privileged functions
* can only be called by guardian. The Strategy serves the Vault, so it
* is subject to guardian defined by the Vault.
*/
function guardian() external view returns (address);
}
/**
* This interface is here for the keeper bot to use.
*/
interface StrategyAPI {
function name() external view returns (string memory);
function vault() external view returns (address);
function want() external view returns (address);
function apiVersion() external pure returns (string memory);
function keeper() external view returns (address);
function isActive() external view returns (bool);
function delegatedAssets() external view returns (uint256);
function estimatedTotalAssets() external view returns (uint256);
function tendTrigger(uint256 callCost) external view returns (bool);
function tend() external;
function harvestTrigger(uint256 callCost) external view returns (bool);
function harvest() external;
event Harvested(uint256 profit, uint256 loss, uint256 debtPayment, uint256 debtOutstanding);
}
interface HealthCheck {
function check(
uint256 profit,
uint256 loss,
uint256 debtPayment,
uint256 debtOutstanding,
uint256 totalDebt
) external view returns (bool);
}
/**
* @title Yearn Base Strategy
* @author yearn.finance
* @notice
* BaseStrategy implements all of the required functionality to interoperate
* closely with the Vault contract. This contract should be inherited and the
* abstract methods implemented to adapt the Strategy to the particular needs
* it has to create a return.
*
* Of special interest is the relationship between `harvest()` and
* `vault.report()'. `harvest()` may be called simply because enough time has
* elapsed since the last report, and not because any funds need to be moved
* or positions adjusted. This is critical so that the Vault may maintain an
* accurate picture of the Strategy's performance. See `vault.report()`,
* `harvest()`, and `harvestTrigger()` for further details.
*/
abstract contract BaseStrategy {
using SafeERC20 for IERC20;
string public metadataURI;
// health checks
bool public doHealthCheck;
address public healthCheck;
/**
* @notice
* Used to track which version of `StrategyAPI` this Strategy
* implements.
* @dev The Strategy's version must match the Vault's `API_VERSION`.
* @return A string which holds the current API version of this contract.
*/
function apiVersion() public pure returns (string memory) {
return "0.4.3";
}
/**
* @notice This Strategy's name.
* @dev
* You can use this field to manage the "version" of this Strategy, e.g.
* `StrategySomethingOrOtherV1`. However, "API Version" is managed by
* `apiVersion()` function above.
* @return This Strategy's name.
*/
function name() external view virtual returns (string memory);
/**
* @notice
* The amount (priced in want) of the total assets managed by this strategy should not count
* towards Yearn's TVL calculations.
* @dev
* You can override this field to set it to a non-zero value if some of the assets of this
* Strategy is somehow delegated inside another part of of Yearn's ecosystem e.g. another Vault.
* Note that this value must be strictly less than or equal to the amount provided by
* `estimatedTotalAssets()` below, as the TVL calc will be total assets minus delegated assets.
* Also note that this value is used to determine the total assets under management by this
* strategy, for the purposes of computing the management fee in `Vault`
* @return
* The amount of assets this strategy manages that should not be included in Yearn's Total Value
* Locked (TVL) calculation across it's ecosystem.
*/
function delegatedAssets() external view virtual returns (uint256) {
return 0;
}
VaultAPI public vault;
address public strategist;
address public rewards;
address public keeper;
IERC20 public want;
// So indexers can keep track of this
event Harvested(uint256 profit, uint256 loss, uint256 debtPayment, uint256 debtOutstanding);
event UpdatedStrategist(address newStrategist);
event UpdatedKeeper(address newKeeper);
event UpdatedRewards(address rewards);
event UpdatedMinReportDelay(uint256 delay);
event UpdatedMaxReportDelay(uint256 delay);
event UpdatedProfitFactor(uint256 profitFactor);
event UpdatedDebtThreshold(uint256 debtThreshold);
event EmergencyExitEnabled();
event UpdatedMetadataURI(string metadataURI);
event SetHealthCheck(address);
event SetDoHealthCheck(bool);
// The minimum number of seconds between harvest calls. See
// `setMinReportDelay()` for more details.
uint256 public minReportDelay;
// The maximum number of seconds between harvest calls. See
// `setMaxReportDelay()` for more details.
uint256 public maxReportDelay;
// The minimum multiple that `callCost` must be above the credit/profit to
// be "justifiable". See `setProfitFactor()` for more details.
uint256 public profitFactor;
// Use this to adjust the threshold at which running a debt causes a
// harvest trigger. See `setDebtThreshold()` for more details.
uint256 public debtThreshold;
// See note on `setEmergencyExit()`.
bool public emergencyExit;
// modifiers
modifier onlyAuthorized() {
require(msg.sender == strategist || msg.sender == governance(), "!authorized");
_;
}
modifier onlyEmergencyAuthorized() {
require(
msg.sender == strategist || msg.sender == governance() || msg.sender == vault.guardian() || msg.sender == vault.management(),
"!authorized"
);
_;
}
modifier onlyStrategist() {
require(msg.sender == strategist, "!strategist");
_;
}
modifier onlyGovernance() {
require(msg.sender == governance(), "!authorized");
_;
}
modifier onlyKeepers() {
require(
msg.sender == keeper ||
msg.sender == strategist ||
msg.sender == governance() ||
msg.sender == vault.guardian() ||
msg.sender == vault.management(),
"!authorized"
);
_;
}
modifier onlyVaultManagers() {
require(msg.sender == vault.management() || msg.sender == governance(), "!authorized");
_;
}
constructor(address _vault) {
_initialize(_vault, msg.sender, msg.sender, msg.sender);
}
/**
* @notice
* Initializes the Strategy, this is called only once, when the
* contract is deployed.
* @dev `_vault` should implement `VaultAPI`.
* @param _vault The address of the Vault responsible for this Strategy.
* @param _strategist The address to assign as `strategist`.
* The strategist is able to change the reward address
* @param _rewards The address to use for pulling rewards.
* @param _keeper The adddress of the _keeper. _keeper
* can harvest and tend a strategy.
*/
function _initialize(
address _vault,
address _strategist,
address _rewards,
address _keeper
) internal {
require(address(want) == address(0), "Strategy already initialized");
vault = VaultAPI(_vault);
want = IERC20(vault.token());
want.safeApprove(_vault, type(uint256).max); // Give Vault unlimited access (might save gas)
strategist = _strategist;
rewards = _rewards;
keeper = _keeper;
// initialize variables
minReportDelay = 0;
maxReportDelay = 86400;
profitFactor = 100;
debtThreshold = 0;
vault.approve(rewards, type(uint256).max); // Allow rewards to be pulled
}
function setHealthCheck(address _healthCheck) external onlyVaultManagers {
emit SetHealthCheck(_healthCheck);
healthCheck = _healthCheck;
}
function setDoHealthCheck(bool _doHealthCheck) external onlyVaultManagers {
emit SetDoHealthCheck(_doHealthCheck);
doHealthCheck = _doHealthCheck;
}
/**
* @notice
* Used to change `strategist`.
*
* This may only be called by governance or the existing strategist.
* @param _strategist The new address to assign as `strategist`.
*/
function setStrategist(address _strategist) external onlyAuthorized {
require(_strategist != address(0));
strategist = _strategist;
emit UpdatedStrategist(_strategist);
}
/**
* @notice
* Used to change `keeper`.
*
* `keeper` is the only address that may call `tend()` or `harvest()`,
* other than `governance()` or `strategist`. However, unlike
* `governance()` or `strategist`, `keeper` may *only* call `tend()`
* and `harvest()`, and no other authorized functions, following the
* principle of least privilege.
*
* This may only be called by governance or the strategist.
* @param _keeper The new address to assign as `keeper`.
*/
function setKeeper(address _keeper) external onlyAuthorized {
require(_keeper != address(0));
keeper = _keeper;
emit UpdatedKeeper(_keeper);
}
/**
* @notice
* Used to change `rewards`. EOA or smart contract which has the permission
* to pull rewards from the vault.
*
* This may only be called by the strategist.
* @param _rewards The address to use for pulling rewards.
*/
function setRewards(address _rewards) external onlyStrategist {
require(_rewards != address(0));
vault.approve(rewards, 0);
rewards = _rewards;
vault.approve(rewards, type(uint256).max);
emit UpdatedRewards(_rewards);
}
/**
* @notice
* Used to change `minReportDelay`. `minReportDelay` is the minimum number
* of blocks that should pass for `harvest()` to be called.
*
* For external keepers (such as the Keep3r network), this is the minimum
* time between jobs to wait. (see `harvestTrigger()`
* for more details.)
*
* This may only be called by governance or the strategist.
* @param _delay The minimum number of seconds to wait between harvests.
*/
function setMinReportDelay(uint256 _delay) external onlyAuthorized {
minReportDelay = _delay;
emit UpdatedMinReportDelay(_delay);
}
/**
* @notice
* Used to change `maxReportDelay`. `maxReportDelay` is the maximum number
* of blocks that should pass for `harvest()` to be called.
*
* For external keepers (such as the Keep3r network), this is the maximum
* time between jobs to wait. (see `harvestTrigger()`
* for more details.)
*
* This may only be called by governance or the strategist.
* @param _delay The maximum number of seconds to wait between harvests.
*/
function setMaxReportDelay(uint256 _delay) external onlyAuthorized {
maxReportDelay = _delay;
emit UpdatedMaxReportDelay(_delay);
}
/**
* @notice
* Used to change `profitFactor`. `profitFactor` is used to determine
* if it's worthwhile to harvest, given gas costs. (See `harvestTrigger()`
* for more details.)
*
* This may only be called by governance or the strategist.
* @param _profitFactor A ratio to multiply anticipated
* `harvest()` gas cost against.
*/
function setProfitFactor(uint256 _profitFactor) external onlyAuthorized {
profitFactor = _profitFactor;
emit UpdatedProfitFactor(_profitFactor);
}
/**
* @notice
* Sets how far the Strategy can go into loss without a harvest and report
* being required.
*
* By default this is 0, meaning any losses would cause a harvest which
* will subsequently report the loss to the Vault for tracking. (See
* `harvestTrigger()` for more details.)
*
* This may only be called by governance or the strategist.
* @param _debtThreshold How big of a loss this Strategy may carry without
* being required to report to the Vault.
*/
function setDebtThreshold(uint256 _debtThreshold) external onlyAuthorized {
debtThreshold = _debtThreshold;
emit UpdatedDebtThreshold(_debtThreshold);
}
/**
* @notice
* Used to change `metadataURI`. `metadataURI` is used to store the URI
* of the file describing the strategy.
*
* This may only be called by governance or the strategist.
* @param _metadataURI The URI that describe the strategy.
*/
function setMetadataURI(string calldata _metadataURI) external onlyAuthorized {
metadataURI = _metadataURI;
emit UpdatedMetadataURI(_metadataURI);
}
/**
* Resolve governance address from Vault contract, used to make assertions
* on protected functions in the Strategy.
*/
function governance() internal view returns (address) {
return vault.governance();
}
/**
* @notice
* Provide an accurate conversion from `_amtInWei` (denominated in wei)
* to `want` (using the native decimal characteristics of `want`).
* @dev
* Care must be taken when working with decimals to assure that the conversion
* is compatible. As an example:
*
* given 1e17 wei (0.1 ETH) as input, and want is USDC (6 decimals),
* with USDC/ETH = 1800, this should give back 1800000000 (180 USDC)
*
* @param _amtInWei The amount (in wei/1e-18 ETH) to convert to `want`
* @return The amount in `want` of `_amtInEth` converted to `want`
**/
function ethToWant(uint256 _amtInWei) public view virtual returns (uint256);
/**
* @notice
* Provide an accurate estimate for the total amount of assets
* (principle + return) that this Strategy is currently managing,
* denominated in terms of `want` tokens.
*
* This total should be "realizable" e.g. the total value that could
* *actually* be obtained from this Strategy if it were to divest its
* entire position based on current on-chain conditions.
* @dev
* Care must be taken in using this function, since it relies on external
* systems, which could be manipulated by the attacker to give an inflated
* (or reduced) value produced by this function, based on current on-chain
* conditions (e.g. this function is possible to influence through
* flashloan attacks, oracle manipulations, or other DeFi attack
* mechanisms).
*
* It is up to governance to use this function to correctly order this
* Strategy relative to its peers in the withdrawal queue to minimize
* losses for the Vault based on sudden withdrawals. This value should be
* higher than the total debt of the Strategy and higher than its expected
* value to be "safe".
* @return The estimated total assets in this Strategy.
*/
function estimatedTotalAssets() public view virtual returns (uint256);
/*
* @notice
* Provide an indication of whether this strategy is currently "active"
* in that it is managing an active position, or will manage a position in
* the future. This should correlate to `harvest()` activity, so that Harvest
* events can be tracked externally by indexing agents.
* @return True if the strategy is actively managing a position.
*/
function isActive() public view returns (bool) {
return vault.strategies(address(this)).debtRatio > 0 || estimatedTotalAssets() > 0;
}
/**
* Perform any Strategy unwinding or other calls necessary to capture the
* "free return" this Strategy has generated since the last time its core
* position(s) were adjusted. Examples include unwrapping extra rewards.
* This call is only used during "normal operation" of a Strategy, and
* should be optimized to minimize losses as much as possible.
*
* This method returns any realized profits and/or realized losses
* incurred, and should return the total amounts of profits/losses/debt
* payments (in `want` tokens) for the Vault's accounting (e.g.
* `want.balanceOf(this) >= _debtPayment + _profit`).
*
* `_debtOutstanding` will be 0 if the Strategy is not past the configured
* debt limit, otherwise its value will be how far past the debt limit
* the Strategy is. The Strategy's debt limit is configured in the Vault.
*
* NOTE: `_debtPayment` should be less than or equal to `_debtOutstanding`.
* It is okay for it to be less than `_debtOutstanding`, as that
* should only used as a guide for how much is left to pay back.
* Payments should be made to minimize loss from slippage, debt,
* withdrawal fees, etc.
*
* See `vault.debtOutstanding()`.
*/
function prepareReturn(uint256 _debtOutstanding)
internal
virtual
returns (
uint256 _profit,
uint256 _loss,
uint256 _debtPayment
);
/**
* Perform any adjustments to the core position(s) of this Strategy given
* what change the Vault made in the "investable capital" available to the
* Strategy. Note that all "free capital" in the Strategy after the report
* was made is available for reinvestment. Also note that this number
* could be 0, and you should handle that scenario accordingly.
*
* See comments regarding `_debtOutstanding` on `prepareReturn()`.
*/
function adjustPosition(uint256 _debtOutstanding) internal virtual;
/**
* Liquidate up to `_amountNeeded` of `want` of this strategy's positions,
* irregardless of slippage. Any excess will be re-invested with `adjustPosition()`.
* This function should return the amount of `want` tokens made available by the
* liquidation. If there is a difference between them, `_loss` indicates whether the
* difference is due to a realized loss, or if there is some other sitution at play
* (e.g. locked funds) where the amount made available is less than what is needed.
*
* NOTE: The invariant `_liquidatedAmount + _loss <= _amountNeeded` should always be maintained
*/
function liquidatePosition(uint256 _amountNeeded) internal virtual returns (uint256 _liquidatedAmount, uint256 _loss);
/**
* Liquidate everything and returns the amount that got freed.
* This function is used during emergency exit instead of `prepareReturn()` to
* liquidate all of the Strategy's positions back to the Vault.
*/
function liquidateAllPositions() internal virtual returns (uint256 _amountFreed);
/**
* @notice
* Provide a signal to the keeper that `tend()` should be called. The
* keeper will provide the estimated gas cost that they would pay to call
* `tend()`, and this function should use that estimate to make a
* determination if calling it is "worth it" for the keeper. This is not
* the only consideration into issuing this trigger, for example if the
* position would be negatively affected if `tend()` is not called
* shortly, then this can return `true` even if the keeper might be
* "at a loss" (keepers are always reimbursed by Yearn).
* @dev
* `callCostInWei` must be priced in terms of `wei` (1e-18 ETH).
*
* This call and `harvestTrigger()` should never return `true` at the same
* time.
* @param callCostInWei The keeper's estimated gas cost to call `tend()` (in wei).
* @return `true` if `tend()` should be called, `false` otherwise.
*/
function tendTrigger(uint256 callCostInWei) public view virtual returns (bool) {
// We usually don't need tend, but if there are positions that need
// active maintainence, overriding this function is how you would
// signal for that.
// If your implementation uses the cost of the call in want, you can
// use uint256 callCost = ethToWant(callCostInWei);
return false;
}
/**
* @notice
* Adjust the Strategy's position. The purpose of tending isn't to
* realize gains, but to maximize yield by reinvesting any returns.
*
* See comments on `adjustPosition()`.
*
* This may only be called by governance, the strategist, or the keeper.
*/
function tend() external onlyKeepers {
// Don't take profits with this call, but adjust for better gains
adjustPosition(vault.debtOutstanding());
}
/**
* @notice
* Provide a signal to the keeper that `harvest()` should be called. The
* keeper will provide the estimated gas cost that they would pay to call
* `harvest()`, and this function should use that estimate to make a
* determination if calling it is "worth it" for the keeper. This is not
* the only consideration into issuing this trigger, for example if the
* position would be negatively affected if `harvest()` is not called
* shortly, then this can return `true` even if the keeper might be "at a
* loss" (keepers are always reimbursed by Yearn).
* @dev
* `callCostInWei` must be priced in terms of `wei` (1e-18 ETH).
*
* This call and `tendTrigger` should never return `true` at the
* same time.
*
* See `min/maxReportDelay`, `profitFactor`, `debtThreshold` to adjust the
* strategist-controlled parameters that will influence whether this call
* returns `true` or not. These parameters will be used in conjunction
* with the parameters reported to the Vault (see `params`) to determine
* if calling `harvest()` is merited.
*
* It is expected that an external system will check `harvestTrigger()`.
* This could be a script run off a desktop or cloud bot (e.g.
* https://github.com/iearn-finance/yearn-vaults/blob/main/scripts/keep.py),
* or via an integration with the Keep3r network (e.g.
* https://github.com/Macarse/GenericKeep3rV2/blob/master/contracts/keep3r/GenericKeep3rV2.sol).
* @param callCostInWei The keeper's estimated gas cost to call `harvest()` (in wei).
* @return `true` if `harvest()` should be called, `false` otherwise.
*/
function harvestTrigger(uint256 callCostInWei) public view virtual returns (bool) {
uint256 callCost = ethToWant(callCostInWei);
StrategyParams memory params = vault.strategies(address(this));
// Should not trigger if Strategy is not activated
if (params.activation == 0) return false;
// Should not trigger if we haven't waited long enough since previous harvest
if (block.timestamp - params.lastReport < minReportDelay) return false;
// Should trigger if hasn't been called in a while
if (block.timestamp - params.lastReport >= maxReportDelay) return true;
// If some amount is owed, pay it back
// NOTE: Since debt is based on deposits, it makes sense to guard against large
// changes to the value from triggering a harvest directly through user
// behavior. This should ensure reasonable resistance to manipulation
// from user-initiated withdrawals as the outstanding debt fluctuates.
uint256 outstanding = vault.debtOutstanding();
if (outstanding > debtThreshold) return true;
// Check for profits and losses
uint256 total = estimatedTotalAssets();
// Trigger if we have a loss to report
if (total + debtThreshold < params.totalDebt) return true;
uint256 profit = 0;
if (total > params.totalDebt) profit = total - params.totalDebt; // We've earned a profit!
// Otherwise, only trigger if it "makes sense" economically (gas cost
// is <N% of value moved)
uint256 credit = vault.creditAvailable();
return (profitFactor * callCost < credit + profit);
}
/**
* @notice
* Harvests the Strategy, recognizing any profits or losses and adjusting
* the Strategy's position.
*
* In the rare case the Strategy is in emergency shutdown, this will exit
* the Strategy's position.
*
* This may only be called by governance, the strategist, or the keeper.
* @dev
* When `harvest()` is called, the Strategy reports to the Vault (via
* `vault.report()`), so in some cases `harvest()` must be called in order
* to take in profits, to borrow newly available funds from the Vault, or
* otherwise adjust its position. In other cases `harvest()` must be
* called to report to the Vault on the Strategy's position, especially if
* any losses have occurred.
*/
function harvest() external onlyKeepers {
uint256 profit = 0;
uint256 loss = 0;
uint256 debtOutstanding = vault.debtOutstanding();
uint256 debtPayment = 0;
if (emergencyExit) {
// Free up as much capital as possible
uint256 amountFreed = liquidateAllPositions();
if (amountFreed < debtOutstanding) {
loss = debtOutstanding - amountFreed;
} else if (amountFreed > debtOutstanding) {
profit = amountFreed - debtOutstanding;
}
debtPayment = debtOutstanding - loss;
} else {
// Free up returns for Vault to pull
(profit, loss, debtPayment) = prepareReturn(debtOutstanding);
}
// Allow Vault to take up to the "harvested" balance of this contract,
// which is the amount it has earned since the last time it reported to
// the Vault.
uint256 totalDebt = vault.strategies(address(this)).totalDebt;
debtOutstanding = vault.report(profit, loss, debtPayment);
// Check if free returns are left, and re-invest them
adjustPosition(debtOutstanding);
// call healthCheck contract
if (doHealthCheck && healthCheck != address(0)) {
require(HealthCheck(healthCheck).check(profit, loss, debtPayment, debtOutstanding, totalDebt), "!healthcheck");
} else {
emit SetDoHealthCheck(true);
doHealthCheck = true;
}
emit Harvested(profit, loss, debtPayment, debtOutstanding);
}
/**
* @notice
* Withdraws `_amountNeeded` to `vault`.
*
* This may only be called by the Vault.
* @param _amountNeeded How much `want` to withdraw.
* @return _loss Any realized losses
*/
function withdraw(uint256 _amountNeeded) external returns (uint256 _loss) {
require(msg.sender == address(vault), "!vault");
// Liquidate as much as possible to `want`, up to `_amountNeeded`
uint256 amountFreed;
(amountFreed, _loss) = liquidatePosition(_amountNeeded);
// Send it directly back (NOTE: Using `msg.sender` saves some gas here)
want.safeTransfer(msg.sender, amountFreed);
// NOTE: Reinvest anything leftover on next `tend`/`harvest`
}
/**
* Do anything necessary to prepare this Strategy for migration, such as
* transferring any reserve or LP tokens, CDPs, or other tokens or stores of
* value.
*/
function prepareMigration(address _newStrategy) internal virtual;
/**
* @notice
* Transfers all `want` from this Strategy to `_newStrategy`.
*
* This may only be called by the Vault.
* @dev
* The new Strategy's Vault must be the same as this Strategy's Vault.
* The migration process should be carefully performed to make sure all
* the assets are migrated to the new address, which should have never
* interacted with the vault before.
* @param _newStrategy The Strategy to migrate to.
*/
function migrate(address _newStrategy) external {
require(msg.sender == address(vault));
require(BaseStrategy(_newStrategy).vault() == vault);
prepareMigration(_newStrategy);
want.safeTransfer(_newStrategy, want.balanceOf(address(this)));
}
/**
* @notice
* Activates emergency exit. Once activated, the Strategy will exit its
* position upon the next harvest, depositing all funds into the Vault as
* quickly as is reasonable given on-chain conditions.
*
* This may only be called by governance or the strategist.
* @dev
* See `vault.setEmergencyShutdown()` and `harvest()` for further details.
*/
function setEmergencyExit() external onlyEmergencyAuthorized {
emergencyExit = true;
vault.revokeStrategy();
emit EmergencyExitEnabled();
}
/**
* Override this to add all tokens/tokenized positions this contract
* manages on a *persistent* basis (e.g. not just for swapping back to
* want ephemerally).
*
* NOTE: Do *not* include `want`, already included in `sweep` below.
*
* Example:
* ```
* function protectedTokens() internal override view returns (address[] memory) {
* address[] memory protected = new address[](3);
* protected[0] = tokenA;
* protected[1] = tokenB;
* protected[2] = tokenC;
* return protected;
* }
* ```
*/
function protectedTokens() internal view virtual returns (address[] memory);
/**
* @notice
* Removes tokens from this Strategy that are not the type of tokens
* managed by this Strategy. This may be used in case of accidentally
* sending the wrong kind of token to this Strategy.
*
* Tokens will be sent to `governance()`.
*
* This will fail if an attempt is made to sweep `want`, or any tokens
* that are protected by this Strategy.
*
* This may only be called by governance.
* @dev
* Implement `protectedTokens()` to specify any additional tokens that
* should be protected from sweeping in addition to `want`.
* @param _token The token to transfer out of this vault.
*/
function sweep(address _token) external onlyGovernance {
require(_token != address(want), "!want");
require(_token != address(vault), "!shares");
address[] memory _protectedTokens = protectedTokens();
for (uint256 i; i < _protectedTokens.length; i++) require(_token != _protectedTokens[i], "!protected");
IERC20(_token).safeTransfer(governance(), IERC20(_token).balanceOf(address(this)));
}
}
abstract contract BaseStrategyInitializable is BaseStrategy {
bool public isOriginal = true;
event Cloned(address indexed clone);
constructor(address _vault) BaseStrategy(_vault) {}
function initialize(
address _vault,
address _strategist,
address _rewards,
address _keeper
) external virtual {
_initialize(_vault, _strategist, _rewards, _keeper);
}
function clone(address _vault) external returns (address) {
require(isOriginal, "!clone");
return this.clone(_vault, msg.sender, msg.sender, msg.sender);
}
function clone(
address _vault,
address _strategist,
address _rewards,
address _keeper
) external returns (address newStrategy) {
// Copied from https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol
bytes20 addressBytes = bytes20(address(this));
assembly {
// EIP-1167 bytecode
let clone_code := mload(0x40)
mstore(clone_code, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(clone_code, 0x14), addressBytes)
mstore(add(clone_code, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
newStrategy := create(0, clone_code, 0x37)
}
BaseStrategyInitializable(newStrategy).initialize(_vault, _strategist, _rewards, _keeper);
emit Cloned(newStrategy);
}
}
// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a / b + (a % b == 0 ? 0 : 1);
}
}
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.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 ReentrancyGuard {
// 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;
constructor() {
_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() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
interface RegistryAPI {
function governance() external view returns (address);
function latestVault(address token) external view returns (address);
function numVaults(address token) external view returns (uint256);
function vaults(address token, uint256 deploymentId) external view returns (address);
}
/**
* @title Yearn Base Router
* @author yearn.finance
* @notice
* BaseRouter implements all of the required functionality to interoperate
* closely with the Vault contract. This contract should be inherited and the
* abstract methods implemented to adapt the Router.
* A good starting point to build a router is https://github.com/yearn/brownie-router-mix
*
*/
abstract contract BaseRouter {
using Math for uint256;
// Reduce number of external calls (SLOADs stay the same)
mapping(address => VaultAPI[]) private _cachedVaults;
RegistryAPI public registry;
// ERC20 Unlimited Approvals (short-circuits VaultAPI.transferFrom)
uint256 constant UNLIMITED_APPROVAL = type(uint256).max;
// Sentinal values used to save gas on deposit/withdraw/migrate
// NOTE: DEPOSIT_EVERYTHING == WITHDRAW_EVERYTHING == MIGRATE_EVERYTHING
uint256 constant DEPOSIT_EVERYTHING = type(uint256).max;
uint256 constant WITHDRAW_EVERYTHING = type(uint256).max;
constructor(address _registry) {
// Recommended to use `v2.registry.ychad.eth`
registry = RegistryAPI(_registry);
}
/**
* @notice
* Used to update the yearn registry.
* @param _registry The new _registry address.
*/
function setRegistry(address _registry) external {
require(msg.sender == registry.governance());
// In case you want to override the registry instead of re-deploying
registry = RegistryAPI(_registry);
// Make sure there's no change in governance
// NOTE: Also avoid bricking the router from setting a bad registry
require(msg.sender == registry.governance());
}
/**
* @notice
* Used to get the most revent vault for the token using the registry.
* @return An instance of a VaultAPI
*/
function bestVault(address token) public view virtual returns (VaultAPI) {
return VaultAPI(registry.latestVault(token));
}
/**
* @notice
* Used to get all vaults from the registery for the token
* @return An array containing instances of VaultAPI
*/
function allVaults(address token) public view virtual returns (VaultAPI[] memory) {
uint256 cache_length = _cachedVaults[token].length;
uint256 num_vaults = registry.numVaults(token);
// Use cached
if (cache_length == num_vaults) {
return _cachedVaults[token];
}
VaultAPI[] memory vaults = new VaultAPI[](num_vaults);
for (uint256 vault_id = 0; vault_id < cache_length; vault_id++) {
vaults[vault_id] = _cachedVaults[token][vault_id];
}
for (uint256 vault_id = cache_length; vault_id < num_vaults; vault_id++) {
vaults[vault_id] = VaultAPI(registry.vaults(token, vault_id));
}
return vaults;
}
function _updateVaultCache(address token, VaultAPI[] memory vaults) internal {
// NOTE: even though `registry` is update-able by Yearn, the intended behavior
// is that any future upgrades to the registry will replay the version
// history so that this cached value does not get out of date.
if (vaults.length > _cachedVaults[token].length) {
_cachedVaults[token] = vaults;
}
}
/**
* @notice
* Used to get the balance of an account accross all the vaults for a token.
* @dev will be used to get the router balance using totalVaultBalance(address(this)).
* @param account The address of the account.
* @return balance of token for the account accross all the vaults.
*/
function totalVaultBalance(address token, address account) public view returns (uint256 balance) {
VaultAPI[] memory vaults = allVaults(token);
for (uint256 id = 0; id < vaults.length; id++) {
balance = balance + ((vaults[id].balanceOf(account) * vaults[id].pricePerShare()) / (10**uint256(vaults[id].decimals())));
}
}
/**
* @notice
* Used to get the TVL on the underlying vaults.
* @return assets the sum of all the assets managed by the underlying vaults.
*/
function totalAssets(address token) public view returns (uint256 assets) {
VaultAPI[] memory vaults = allVaults(token);
for (uint256 id = 0; id < vaults.length; id++) {
assets = assets + vaults[id].totalAssets();
}
}
function _deposit(
IERC20 token,
address depositor,
address receiver,
uint256 amount, // if `MAX_UINT256`, just deposit everything
bool pullFunds // If true, funds need to be pulled from `depositor` via `transferFrom`
) internal returns (uint256 deposited) {
VaultAPI _bestVault = bestVault(address(token));
if (pullFunds) {
if (amount == DEPOSIT_EVERYTHING) {
amount = token.balanceOf(depositor);
}
SafeERC20.safeTransferFrom(token, depositor, address(this), amount);
}
if (token.allowance(address(this), address(_bestVault)) < amount) {
SafeERC20.safeApprove(token, address(_bestVault), 0); // Avoid issues with some tokens requiring 0
SafeERC20.safeApprove(token, address(_bestVault), UNLIMITED_APPROVAL); // Vaults are trusted
}
// Depositing returns number of shares deposited
// NOTE: Shortcut here is assuming the number of tokens deposited is equal to the
// number of shares credited, which helps avoid an occasional multiplication
// overflow if trying to adjust the number of shares by the share price.
uint256 beforeBal = token.balanceOf(address(this));
if (receiver != address(this)) {
_bestVault.deposit(amount, receiver);
} else if (amount != DEPOSIT_EVERYTHING) {
_bestVault.deposit(amount);
} else {
_bestVault.deposit();
}
uint256 afterBal = token.balanceOf(address(this));
deposited = beforeBal - afterBal;
// `receiver` now has shares of `_bestVault` as balance, converted to `token` here
// Issue a refund if not everything was deposited
if (depositor != address(this) && afterBal > 0) SafeERC20.safeTransfer(token, depositor, afterBal);
}
function _withdraw(
IERC20 token,
address sender,
address receiver,
uint256 amount, // if `MAX_UINT256`, just withdraw everything
bool withdrawFromBest // If true, also withdraw from `_bestVault`
) internal returns (uint256 withdrawn) {
VaultAPI _bestVault = bestVault(address(token));
VaultAPI[] memory vaults = allVaults(address(token));
_updateVaultCache(address(token), vaults);
// NOTE: This loop will attempt to withdraw from each Vault in `allVaults` that `sender`
// is deposited in, up to `amount` tokens. The withdraw action can be expensive,
// so it if there is a denial of service issue in withdrawing, the downstream usage
// of this router contract must give an alternative method of withdrawing using
// this function so that `amount` is less than the full amount requested to withdraw
// (e.g. "piece-wise withdrawals"), leading to less loop iterations such that the
// DoS issue is mitigated (at a tradeoff of requiring more txns from the end user).
for (uint256 id = 0; id < vaults.length; id++) {
if (!withdrawFromBest && vaults[id] == _bestVault) {
continue; // Don't withdraw from the best
}
// Start with the total shares that `sender` has
uint256 availableShares = vaults[id].balanceOf(sender);
// Restrict by the allowance that `sender` has to this contract
// NOTE: No need for allowance check if `sender` is this contract
if (sender != address(this)) {
availableShares = Math.min(availableShares, vaults[id].allowance(sender, address(this)));
}
// Limit by maximum withdrawal size from each vault
availableShares = Math.min(availableShares, vaults[id].maxAvailableShares());
if (availableShares > 0) {
// Intermediate step to move shares to this contract before withdrawing
// NOTE: No need for share transfer if this contract is `sender`
if (amount != WITHDRAW_EVERYTHING) {
// Compute amount to withdraw fully to satisfy the request
uint256 estimatedShares = ((amount - withdrawn) * (10**uint256(vaults[id].decimals()))) / vaults[id].pricePerShare();
// NOTE: Changes every iteration
// Limit amount to withdraw to the maximum made available to this contract
// NOTE: Avoid corner case where `estimatedShares` isn't precise enough
// NOTE: If `0 < estimatedShares < 1` but `availableShares > 1`, this will withdraw more than necessary
if (estimatedShares > 0 && estimatedShares < availableShares) {
if (sender != address(this)) vaults[id].transferFrom(sender, address(this), estimatedShares);
withdrawn = withdrawn + vaults[id].withdraw(estimatedShares);
} else {
if (sender != address(this)) vaults[id].transferFrom(sender, address(this), availableShares);
withdrawn = withdrawn + vaults[id].withdraw(availableShares);
}
} else {
if (sender != address(this)) vaults[id].transferFrom(sender, address(this), availableShares);
withdrawn = withdrawn + vaults[id].withdraw();
}
// Check if we have fully satisfied the request
// NOTE: use `amount = WITHDRAW_EVERYTHING` for withdrawing everything
if (amount <= withdrawn) break; // withdrawn as much as we needed
}
}
// If we have extra, deposit back into `_bestVault` for `sender`
// NOTE: Invariant is `withdrawn <= amount`
if (withdrawn > amount && ((withdrawn - amount) > _bestVault.pricePerShare() / 10**_bestVault.decimals())) {
// Don't forget to approve the deposit
if (token.allowance(address(this), address(_bestVault)) < withdrawn - amount) {
SafeERC20.safeApprove(token, address(_bestVault), UNLIMITED_APPROVAL); // Vaults are trusted
}
_bestVault.deposit(withdrawn - amount, sender);
withdrawn = amount;
}
// `receiver` now has `withdrawn` tokens as balance
if (receiver != address(this)) SafeERC20.safeTransfer(token, receiver, withdrawn);
}
}
/**
* @title Yearn Base Wrapper
* @author yearn.finance
* @notice
* BaseWrapper implements all of the required functionality to interoperate
* closely with the Vault contract. This contract should be inherited and the
* abstract methods implemented to adapt the Wrapper.
* A good starting point to build a wrapper is https://github.com/yearn/brownie-wrapper-mix
*
*/
abstract contract BaseWrapper is BaseRouter {
using Math for uint256;
IERC20 public token;
uint256 constant MIGRATE_EVERYTHING = type(uint256).max;
// VaultsAPI.depositLimit is unlimited
uint256 constant UNCAPPED_DEPOSITS = type(uint256).max;
constructor(address _token, address _registry) BaseRouter(_registry) {
// Recommended to use a token with a `Registry.latestVault(_token) != address(0)`
token = IERC20(_token);
}
/**
* @notice
* Used to get the most recent vault for the token using the registry.
* @return An instance of a VaultAPI
*/
function bestVault() public view virtual returns (VaultAPI) {
return bestVault(address(token));
}
/**
* @notice
* Used to get all vaults from the registery for the token
* @return An array containing instances of VaultAPI
*/
function allVaults() public view virtual returns (VaultAPI[] memory) {
return allVaults(address(token));
}
/**
* @notice
* Used to get the balance of an account accross all the vaults for a token.
* @dev will be used to get the wrapper balance using totalVaultBalance(address(this)).
* @param account The address of the account.
* @return balance of token for the account accross all the vaults.
*/
function totalVaultBalance(address account) public view returns (uint256) {
return totalVaultBalance(address(token), account);
}
/**
* @notice
* Used to get the TVL on the underlying vaults.
* @return assets the sum of all the assets managed by the underlying vaults.
*/
function totalAssets() public view returns (uint256) {
return totalAssets(address(token));
}
function _deposit(
address depositor,
address receiver,
uint256 amount, // if `MAX_UINT256`, just deposit everything
bool pullFunds // If true, funds need to be pulled from `depositor` via `transferFrom`
) internal returns (uint256) {
return _deposit(token, depositor, receiver, amount, pullFunds);
}
function _withdraw(
address sender,
address receiver,
uint256 amount, // if `MAX_UINT256`, just withdraw everything
bool withdrawFromBest // If true, also withdraw from `_bestVault`
) internal returns (uint256) {
return _withdraw(token, sender, receiver, amount, withdrawFromBest);
}
function _migrate(address account) internal returns (uint256) {
return _migrate(account, MIGRATE_EVERYTHING);
}
function _migrate(address account, uint256 amount) internal returns (uint256) {
// NOTE: In practice, it was discovered that <50 was the maximum we've see for this variance
return _migrate(account, amount, 0);
}
function _migrate(
address account,
uint256 amount,
uint256 maxMigrationLoss
) internal returns (uint256 migrated) {
VaultAPI _bestVault = bestVault();
// NOTE: Only override if we aren't migrating everything
uint256 _depositLimit = _bestVault.depositLimit();
uint256 _totalAssets = _bestVault.totalAssets();
if (_depositLimit <= _totalAssets) return 0; // Nothing to migrate (not a failure)
uint256 _amount = amount;
if (_depositLimit < UNCAPPED_DEPOSITS && _amount < WITHDRAW_EVERYTHING) {
// Can only deposit up to this amount
uint256 _depositLeft = _depositLimit - _totalAssets;
if (_amount > _depositLeft) _amount = _depositLeft;
}
if (_amount > 0) {
// NOTE: `false` = don't withdraw from `_bestVault`
uint256 withdrawn = _withdraw(token, account, address(this), _amount, false);
if (withdrawn == 0) return 0; // Nothing to migrate (not a failure)
// NOTE: `false` = don't do `transferFrom` because it's already local
migrated = _deposit(token, address(this), account, withdrawn, false);
// NOTE: Due to the precision loss of certain calculations, there is a small inefficency
// on how migrations are calculated, and this could lead to a DoS issue. Hence, this
// value is made to be configurable to allow the user to specify how much is acceptable
require((withdrawn - migrated) <= maxMigrationLoss);
} // else: nothing to migrate! (not a failure)
}
}
contract yToken is IERC20, BaseWrapper {
mapping(address => mapping(address => uint256)) public override allowance;
constructor(address _token, address _registry) BaseWrapper(_token, _registry) {}
function name() external view returns (string memory) {
return string(abi.encodePacked("Yearn ", IERC20Metadata(address(token)).name()));
}
function symbol() external view returns (string memory) {
return string(abi.encodePacked("y", IERC20Metadata(address(token)).symbol()));
}
function decimals() external view returns (uint256) {
return IERC20Metadata(address(token)).decimals();
}
function totalSupply() external view override returns (uint256 total) {
return totalAssets();
}
function balanceOf(address account) external view override returns (uint256 balance) {
return totalVaultBalance(account);
}
function _transfer(
address sender,
address receiver,
uint256 amount
) internal {
require(receiver != address(0), "ERC20: transfer to the zero address");
require(amount == _withdraw(sender, receiver, amount, true)); // `true` means use `bestVault`
emit Transfer(sender, receiver, amount);
}
function transfer(address receiver, uint256 amount) public virtual override returns (bool) {
_transfer(msg.sender, receiver, amount);
return true;
}
function _approve(
address owner,
address spender,
uint256 amount
) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
allowance[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function approve(address spender, uint256 amount) public override returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
function transferFrom(
address sender,
address receiver,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, receiver, amount);
_approve(sender, msg.sender, allowance[sender][msg.sender] - amount);
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(msg.sender, spender, allowance[msg.sender][spender] + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(msg.sender, spender, allowance[msg.sender][spender] - subtractedValue);
return true;
}
function deposit(uint256 amount) external returns (uint256) {
return _deposit(msg.sender, msg.sender, amount, true); // `true` = pull from sender
}
function withdraw(uint256 amount) external returns (uint256) {
return _withdraw(msg.sender, msg.sender, amount, true); // `true` = withdraw from `bestVault`
}
function _permitAll(
address user,
VaultAPI[] calldata vaults,
bytes[] calldata signatures
) internal {
require(vaults.length == signatures.length);
for (uint256 i = 0; i < vaults.length; i++) {
require(vaults[i].permit(user, address(this), type(uint256).max, 0, signatures[i]));
}
}
function permitAll(VaultAPI[] calldata vaults, bytes[] calldata signatures) public {
_permitAll(msg.sender, vaults, signatures);
}
function migrate() external returns (uint256) {
return _migrate(msg.sender);
}
function migrate(uint256 amount) external returns (uint256) {
return _migrate(msg.sender, amount);
}
function migrate(uint256 amount, uint256 maxMigrationLoss) external returns (uint256) {
return _migrate(msg.sender, amount, maxMigrationLoss);
}
function migrate(VaultAPI[] calldata vaults, bytes[] calldata signatures) external returns (uint256) {
_permitAll(msg.sender, vaults, signatures);
return _migrate(msg.sender);
}
function migrate(
VaultAPI[] calldata vaults,
bytes[] calldata signatures,
uint256 amount
) external returns (uint256) {
_permitAll(msg.sender, vaults, signatures);
return _migrate(msg.sender, amount);
}
function migrate(
VaultAPI[] calldata vaults,
bytes[] calldata signatures,
address user,
uint256 amount
) external returns (uint256) {
_permitAll(user, vaults, signatures);
return _migrate(user, amount);
}
function revokeAll(VaultAPI[] calldata vaults, bytes[] calldata signatures) external {
require(vaults.length == signatures.length);
for (uint256 i = 0; i < vaults.length; i++) {
require(vaults[i].permit(msg.sender, address(this), 0, 0, signatures[i]));
}
}
}
interface IWETH {
function deposit() external payable;
function withdraw(uint256 wad) external;
}
contract yWETH is ReentrancyGuard, yToken {
using Address for address payable;
constructor(address _weth, address _registry) yToken(_weth, _registry) {}
function depositETH() public payable returns (uint256) {
uint256 amount = msg.value;
// NOTE: `BaseWrapper.token` is WETH
IWETH(address(token)).deposit{value: amount}();
// NOTE: Deposit handles approvals
// NOTE: Need to use different method to deposit than `yToken`
return _deposit(address(this), msg.sender, amount, false); // `false` = pull from `this`
}
function withdrawETH(uint256 amount) external nonReentrant returns (uint256 withdrawn) {
// NOTE: Need to use different method to withdraw than `yToken`
withdrawn = _withdraw(msg.sender, address(this), amount, true); // `true` = withdraw from `bestVault`
// NOTE: `BaseWrapper.token` is WETH
IWETH(address(token)).withdraw(withdrawn);
// NOTE: Any unintentionally
payable(msg.sender).sendValue(address(this).balance);
}
receive() external payable {
if (msg.sender != address(token)) {
depositETH();
} // else: WETH is sending us back ETH, so don't do anything (to avoid recursion)
}
}
interface IBalancerPool is IERC20 {
enum SwapKind {
GIVEN_IN,
GIVEN_OUT
}
struct SwapRequest {
SwapKind kind;
IERC20 tokenIn;
IERC20 tokenOut;
uint256 amount;
// Misc data
bytes32 poolId;
uint256 lastChangeBlock;
address from;
address to;
bytes userData;
}
// virtual price of bpt
function getRate() external view returns (uint256);
function getPoolId() external view returns (bytes32 poolId);
function symbol() external view returns (string memory s);
function onSwap(
SwapRequest memory swapRequest,
uint256[] memory balances,
uint256 indexIn,
uint256 indexOut
) external view returns (uint256 amount);
}
interface IAsset {
// solhint-disable-previous-line no-empty-blocks
}
interface IBalancerVault {
enum PoolSpecialization {
GENERAL,
MINIMAL_SWAP_INFO,
TWO_TOKEN
}
enum JoinKind {
INIT,
EXACT_TOKENS_IN_FOR_BPT_OUT,
TOKEN_IN_FOR_EXACT_BPT_OUT,
ALL_TOKENS_IN_FOR_EXACT_BPT_OUT
}
enum ExitKind {
EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,
EXACT_BPT_IN_FOR_TOKENS_OUT,
BPT_IN_FOR_EXACT_TOKENS_OUT
}
enum SwapKind {
GIVEN_IN,
GIVEN_OUT
}
/**
* @dev Data for each individual swap executed by `batchSwap`. The asset in and out fields are indexes into the
* `assets` array passed to that function, and ETH assets are converted to WETH.
*
* If `amount` is zero, the multihop mechanism is used to determine the actual amount based on the amount in/out
* from the previous swap, depending on the swap kind.
*
* The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be
* used to extend swap behavior.
*/
struct BatchSwapStep {
bytes32 poolId;
uint256 assetInIndex;
uint256 assetOutIndex;
uint256 amount;
bytes userData;
}
/**
* @dev All tokens in a swap are either sent from the `sender` account to the Vault, or from the Vault to the
* `recipient` account.
*
* If the caller is not `sender`, it must be an authorized relayer for them.
*
* If `fromInternalBalance` is true, the `sender`'s Internal Balance will be preferred, performing an ERC20
* transfer for the difference between the requested amount and the User's Internal Balance (if any). The `sender`
* must have allowed the Vault to use their tokens via `IERC20.approve()`. This matches the behavior of
* `joinPool`.
*
* If `toInternalBalance` is true, tokens will be deposited to `recipient`'s internal balance instead of
* transferred. This matches the behavior of `exitPool`.
*
* Note that ETH cannot be deposited to or withdrawn from Internal Balance: attempting to do so will trigger a
* revert.
*/
struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}
/**
* @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on
* the `kind` value.
*
* `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address).
* Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault.
*
* The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be
* used to extend swap behavior.
*/
struct SingleSwap {
bytes32 poolId;
SwapKind kind;
IAsset assetIn;
IAsset assetOut;
uint256 amount;
bytes userData;
}
// enconding formats https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/balancer-js/src/pool-weighted/encoder.ts
struct JoinPoolRequest {
IAsset[] assets;
uint256[] maxAmountsIn;
bytes userData;
bool fromInternalBalance;
}
struct ExitPoolRequest {
IAsset[] assets;
uint256[] minAmountsOut;
bytes userData;
bool toInternalBalance;
}
function joinPool(
bytes32 poolId,
address sender,
address recipient,
JoinPoolRequest memory request
) external payable;
function exitPool(
bytes32 poolId,
address sender,
address payable recipient,
ExitPoolRequest calldata request
) external;
function getPool(bytes32 poolId)
external
view
returns (address poolAddress, PoolSpecialization);
function getPoolTokenInfo(bytes32 poolId, IERC20 token)
external
view
returns (
uint256 cash,
uint256 managed,
uint256 lastChangeBlock,
address assetManager
);
function getPoolTokens(bytes32 poolId)
external
view
returns (
IERC20[] calldata tokens,
uint256[] calldata balances,
uint256 lastChangeBlock
);
/**
* @dev Performs a swap with a single Pool.
*
* If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens
* taken from the Pool, which must be greater than or equal to `limit`.
*
* If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens
* sent to the Pool, which must be less than or equal to `limit`.
*
* Internal Balance usage and the recipient are determined by the `funds` struct.
*
* Emits a `Swap` event.
*/
function swap(
SingleSwap memory singleSwap,
FundManagement memory funds,
uint256 limit,
uint256 deadline
) external returns (uint256 amountCalculated);
/**
* @dev Performs a series of swaps with one or multiple Pools. In each individual swap, the caller determines either
* the amount of tokens sent to or received from the Pool, depending on the `kind` value.
*
* Returns an array with the net Vault asset balance deltas. Positive amounts represent tokens (or ETH) sent to the
* Vault, and negative amounts represent tokens (or ETH) sent by the Vault. Each delta corresponds to the asset at
* the same index in the `assets` array.
*
* Swaps are executed sequentially, in the order specified by the `swaps` array. Each array element describes a
* Pool, the token to be sent to this Pool, the token to receive from it, and an amount that is either `amountIn` or
* `amountOut` depending on the swap kind.
*
* Multihop swaps can be executed by passing an `amount` value of zero for a swap. This will cause the amount in/out
* of the previous swap to be used as the amount in for the current one. In a 'given in' swap, 'tokenIn' must equal
* the previous swap's `tokenOut`. For a 'given out' swap, `tokenOut` must equal the previous swap's `tokenIn`.
*
* The `assets` array contains the addresses of all assets involved in the swaps. These are either token addresses,
* or the IAsset sentinel value for ETH (the zero address). Each entry in the `swaps` array specifies tokens in and
* out by referencing an index in `assets`. Note that Pools never interact with ETH directly: it will be wrapped to
* or unwrapped from WETH by the Vault.
*
* Internal Balance usage, sender, and recipient are determined by the `funds` struct. The `limits` array specifies
* the minimum or maximum amount of each token the vault is allowed to transfer.
*
* `batchSwap` can be used to make a single swap, like `swap` does, but doing so requires more gas than the
* equivalent `swap` call.
*
* Emits `Swap` events.
*/
function batchSwap(
SwapKind kind,
BatchSwapStep[] memory swaps,
IAsset[] memory assets,
FundManagement memory funds,
int256[] memory limits,
uint256 deadline
) external payable returns (int256[] memory);
}
interface IVault is IERC20 {
function name() external view returns (string calldata);
function symbol() external view returns (string calldata);
function decimals() external view returns (uint256);
function apiVersion() external pure returns (string memory);
function withdrawalQueue(uint256 i) external view returns (address);
function totalDebt() external view returns (uint256);
function permit(
address owner,
address spender,
uint256 amount,
uint256 expiry,
bytes calldata signature
) external returns (bool);
function initialize(
address token,
address governance,
address rewards,
string memory name,
string memory symbol,
address guardian,
address management
) external;
function addStrategy(
address _strategy,
uint256 _debtRatio,
uint256 _minDebtPerHarvest,
uint256 _maxDebtPerHarvest,
uint256 _performanceFee
) external;
function setDepositLimit(uint256 amount) external;
// NOTE: Vyper produces multiple signatures for a given function with "default" args
function deposit() external returns (uint256);
function deposit(uint256 amount) external returns (uint256);
function deposit(uint256 amount, address recipient)
external
returns (uint256);
// NOTE: Vyper produces multiple signatures for a given function with "default" args
function withdraw() external returns (uint256);
function withdraw(uint256 maxShares) external returns (uint256);
function withdraw(uint256 maxShares, address recipient)
external
returns (uint256);
function token() external view returns (address);
function strategies(address _strategy)
external
view
returns (StrategyParams memory);
function pricePerShare() external view returns (uint256);
function totalAssets() external view returns (uint256);
function depositLimit() external view returns (uint256);
function maxAvailableShares() external view returns (uint256);
/**
* View how much the Vault would increase this Strategy's borrow limit,
* based on its present performance (since its last report). Can be used to
* determine expectedReturn in your Strategy.
*/
function creditAvailable() external view returns (uint256);
/**
* View how much the Vault would like to pull back from the Strategy,
* based on its present performance (since its last report). Can be used to
* determine expectedReturn in your Strategy.
*/
function debtOutstanding() external view returns (uint256);
function debtOutstanding(address _strategy) external view returns (uint256);
/**
* View how much the Vault expect this Strategy to return at the current
* block, based on its present performance (since its last report). Can be
* used to determine expectedReturn in your Strategy.
*/
function expectedReturn() external view returns (uint256);
/**
* This is the main contact point where the Strategy interacts with the
* Vault. It is critical that this call is handled as intended by the
* Strategy. Therefore, this function will be called by BaseStrategy to
* make sure the integration is correct.
*/
function report(
uint256 _gain,
uint256 _loss,
uint256 _debtPayment
) external returns (uint256);
/**
* This function should only be used in the scenario where the Strategy is
* being retired but no migration of the positions are possible, or in the
* extreme scenario that the Strategy needs to be put into "Emergency Exit"
* mode in order for it to exit as quickly as possible. The latter scenario
* could be for any reason that is considered "critical" that the Strategy
* exits its position as fast as possible, such as a sudden change in
* market conditions leading to losses, or an imminent failure in an
* external dependency.
*/
function revokeStrategy() external;
function revokeStrategy(address strategy) external;
function migrateStrategy(address oldVersion, address newVersion) external;
function setEmergencyShutdown(bool active) external;
function setManagementFee(uint256 fee) external;
function updateStrategyDebtRatio(address strategy, uint256 debtRatio)
external;
function withdraw(
uint256 maxShare,
address recipient,
uint256 maxLoss
) external;
/**
* View the governance address of the Vault to assert privileged functions
* can only be called by governance. The Strategy serves the Vault, so it
* is subject to governance defined by the Vault.
*/
function governance() external view returns (address);
/**
* View the management address of the Vault to assert privileged functions
* can only be called by management. The Strategy serves the Vault, so it
* is subject to management defined by the Vault.
*/
function management() external view returns (address);
/**
* View the guardian address of the Vault to assert privileged functions
* can only be called by guardian. The Strategy serves the Vault, so it
* is subject to guardian defined by the Vault.
*/
function guardian() external view returns (address);
}
/* A few key things can change between underlying pools. For example, although we enter
* most pools through balancerVault.joinPool(), linear pools such as the aave boosted pool
* don't support this, and we need to enter them through batch swaps. Common logic inside
* 'BaseSingleSidedBalancer' and details are implemented in extensions such as
* 'PhantomSingleSidedBalancer'.
*/
abstract contract BaseSingleSidedBalancer is BaseStrategy {
using SafeERC20 for IERC20;
using Address for address;
uint256 public lastDepositTime;
IVault public bptVault;
IBalancerPool public balancerPool;
uint8 public numTokens;
uint8 public tokenIndex;
IAsset[] internal assets; // assets of the pool
bytes32 public balancerPoolID;
uint256 public maxSlippageIn; // bips
uint256 public maxSlippageOut; // bips
uint256 public maxSingleInvest;
bool public withdrawProtection;
uint256 internal constant MAX_BPS = 10000;
address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
IERC20 internal constant BAL =
IERC20(0xba100000625a3754423978a60c9317c58a424e3D);
IBalancerVault internal constant balancerVault =
IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
// === DEPLOYMENT FUNCTIONS ===
constructor(address _vault) BaseStrategy(_vault) {}
// === TVL ACCOUNTING ===
function delegatedAssets() public view override returns (uint256) {
return vault.strategies(address(this)).totalDebt;
}
function estimatedTotalAssets() public view override returns (uint256) {
uint256 _totalBPTBalance = balancerPool.balanceOf(address(this)) +
poolTokensInYVault();
return want.balanceOf(address(this)) + bptToWant(_totalBPTBalance);
}
function poolTokensInYVault() public view returns (uint256) {
uint256 _balance = bptVault.balanceOf(address(this));
if (bptVault.totalSupply() == 0) {
//needed because of revert on priceperfullshare if 0
return 0;
}
uint256 _pricePerShare = bptVault.pricePerShare();
uint256 _decimals = IERC20Metadata(address(balancerPool)).decimals();
// ASSUMPTION: balancer pool tokens are always 18 decimals
return (_balance * _pricePerShare) / (10 ** _decimals);
}
function bptToWant(uint256 _bptAmount) public view returns (uint256) {
uint256 _unscaledWantAmount = (_bptAmount * balancerPool.getRate()) /
1e18;
return
_scaleDecimals(
_unscaledWantAmount,
IERC20Metadata(address(balancerPool)),
IERC20Metadata(address(want))
);
}
function wantToBPT(uint256 _wantAmount) public view returns (uint256) {
uint256 _unscaledBPTAmount = (_wantAmount * 1e18) /
balancerPool.getRate();
return
_scaleDecimals(
_unscaledBPTAmount,
IERC20Metadata(address(want)),
IERC20Metadata(address(balancerPool))
);
}
function _scaleDecimals(
uint256 _amount,
IERC20Metadata _fromToken,
IERC20Metadata _toToken
) internal view returns (uint256 _scaled) {
uint256 decFrom = _fromToken.decimals();
uint256 decTo = _toToken.decimals();
return
decTo > decFrom
? _amount * (10**(decTo - decFrom))
: _amount / (10**(decFrom - decTo));
}
// === HARVEST-RELEVANT FUNCTIONS ===
function investWantIntoBalancerPool(uint256 _wantAmount) internal virtual;
function adjustPosition(uint256 _debtOutstanding) internal override {
uint256 _balanceOfWant = want.balanceOf(address(this));
if (_balanceOfWant > _debtOutstanding) {
uint256 _amountToInvest = _balanceOfWant - _debtOutstanding;
_amountToInvest = Math.min(_amountToInvest, maxSingleInvest);
investWantIntoBalancerPool(_amountToInvest);
bptVault.deposit();
lastDepositTime = block.timestamp;
}
}
function prepareReturn(uint256 _debtOutstanding)
internal
override
returns (
uint256 _profit,
uint256 _loss,
uint256 _debtPayment
)
{
_debtPayment = _debtOutstanding;
uint256 _totalDebt = vault.strategies(address(this)).totalDebt;
uint256 _totalAssets = estimatedTotalAssets();
if (_totalAssets > _totalDebt) {
_profit = _totalAssets - _totalDebt;
} else {
_loss = _totalDebt - _totalAssets;
}
uint256 _amountNeededLiquid = _debtPayment + _profit;
uint256 _liquidAssets = want.balanceOf(address(this));
if (_amountNeededLiquid > _liquidAssets) {
uint256 _toWithdraw = _amountNeededLiquid - _liquidAssets;
(, uint256 _withdrawalLoss) = withdrawSome(_toWithdraw);
if (_withdrawalLoss < _profit) {
_profit = _profit - _withdrawalLoss;
} else {
_loss = _loss + (_withdrawalLoss - _profit);
_profit = 0;
}
_liquidAssets = want.balanceOf(address(this));
// pay back profit first
if (_liquidAssets < _profit) {
_profit = _liquidAssets;
_debtPayment = 0;
} else if (_liquidAssets < _debtPayment + _profit) {
_debtPayment = _liquidAssets - _profit;
}
}
}
function liquidatePosition(uint256 _amountNeeded)
internal
override
returns (uint256 _liquidatedAmount, uint256 _loss)
{
uint256 _liquidAssets = want.balanceOf(address(this));
if (_liquidAssets < _amountNeeded) {
uint256 _toWithdraw = _amountNeeded - _liquidAssets;
(_liquidatedAmount, ) = withdrawSome(_toWithdraw);
}
_liquidatedAmount = Math.min(
_amountNeeded,
_liquidatedAmount + _liquidAssets
);
_loss = _amountNeeded - _liquidatedAmount;
}
// safe to request more than we have
function liquidateBPTsToWant(uint256 _bptAmount) internal virtual;
function withdrawSome(uint256 _amountToWithdraw)
internal
returns (uint256 _liquidatedAmount, uint256 _loss)
{
uint256 _wantBalanceBefore = want.balanceOf(address(this));
uint256 _bptAmount = wantToBPT(_amountToWithdraw);
// should be 0 but just in case
uint256 _bptBeforeBalance = balancerPool.balanceOf(address(this));
uint256 _pricePerShare = bptVault.pricePerShare();
uint256 _vaultShareToWithdraw = (_bptAmount * 1e18) / _pricePerShare;
uint256 _vaultShareBalance = bptVault.balanceOf(address(this));
if (_vaultShareToWithdraw > _vaultShareBalance) {
// this is not loss, so we amend the amount
_vaultShareToWithdraw = _vaultShareBalance;
uint256 _equivalentBPTAmount = (_vaultShareToWithdraw *
_pricePerShare) / 1e18;
_amountToWithdraw = bptToWant(_equivalentBPTAmount);
}
if (_vaultShareToWithdraw > 0) {
bptVault.withdraw(_vaultShareToWithdraw);
if (withdrawProtection) {
//this tests that we liquidated all of the expected ytokens. Without it if we get back less then will mark it is loss
require(
_vaultShareBalance - bptVault.balanceOf(address(this)) >=
_vaultShareToWithdraw - 1,
"YVAULTWITHDRAWFAILED"
);
}
}
uint256 _bptsToLiquidate = balancerPool.balanceOf(address(this)) -
_bptBeforeBalance;
if (_bptsToLiquidate > 0) {
liquidateBPTsToWant(_bptsToLiquidate);
}
uint256 _wantBalanceDiff = want.balanceOf(address(this)) -
_wantBalanceBefore;
if (_wantBalanceDiff >= _amountToWithdraw) {
_liquidatedAmount = _amountToWithdraw;
} else {
_liquidatedAmount = _wantBalanceDiff;
_loss = _amountToWithdraw - _wantBalanceDiff;
}
}
function liquidateAllPositions()
internal
override
returns (uint256 _amountFreed)
{
// slippage checks still exist in emergency exit
bptVault.withdraw(bptVault.balanceOf(address(this)));
liquidateBPTsToWant(balancerPool.balanceOf(address(this)));
return want.balanceOf(address(this));
}
// === MISC FUNCTIONS ===
// Examples: BASIC, PHANTOM
function extensionName() internal view virtual returns (string memory);
// Examples: SSB_DAI_BASIC_FUD, SSB_USDC_PHANTOM_bb-a-USD
function name() external view override returns (string memory) {
return
string(
abi.encodePacked(
"SSB_",
IERC20Metadata(address(want)).symbol(),
"_",
extensionName(),
"_",
balancerPool.symbol()
)
);
}
function prepareMigration(address _newStrategy) internal override {
// everything else can, and should be, swept
bptVault.transfer(_newStrategy, bptVault.balanceOf(address(this)));
}
function protectedTokens()
internal
view
override
returns (address[] memory)
// solhint-disable-next-line no-empty-blocks
{
}
function ethToWant(uint256 _amtInWei)
public
view
virtual
override
returns (uint256)
// solhint-disable-next-line no-empty-blocks
{
}
// === MANAGEMENT FUNCTIONS ===
function updateMaxSlippageIn(uint256 _maxSlippageIn)
public
onlyVaultManagers
{
maxSlippageIn = _maxSlippageIn;
}
function updateMaxSlippageOut(uint256 _maxSlippageOut)
public
onlyVaultManagers
{
maxSlippageOut = _maxSlippageOut;
}
function updateMaxSingleInvest(uint256 _maxSingleInvest)
public
onlyVaultManagers
{
maxSingleInvest = _maxSingleInvest;
}
function updateWithdrawProtection(bool _withdrawProtection)
public
onlyVaultManagers
{
withdrawProtection = _withdrawProtection;
}
}
/* All extensions must implement the following:
* function extensionName() returns (string memory)
* function investWantIntoBalancerPool(uint256 _wantAmount)
* function liquidateBPTsToWant(uint256 _bptAmount)
*
*
* Extensions can optionally add other functions which allow vault managers
* to manually manage the position.
*/
contract BasicSingleSidedBalancer is BaseSingleSidedBalancer {
using SafeERC20 for IERC20;
using Address for address;
event Cloned(address indexed clone);
bool public isOriginal = true;
uint256 internal constant MAX_TOKENS = 20;
// Cloning & initialization code adapted from https://github.com/yearn/yearn-vaults/blob/43a0673ab89742388369bc0c9d1f321aa7ea73f6/contracts/BaseStrategy.sol#L866
constructor(
address _vault,
address _bptVault,
uint256 _maxSlippageIn,
uint256 _maxSlippageOut,
uint256 _maxSingleInvest
) BaseSingleSidedBalancer(_vault) {
_initializeStrat(
_bptVault,
_maxSlippageIn,
_maxSlippageOut,
_maxSingleInvest
);
}
function _initializeStrat(
address _bptVault,
uint256 _maxSlippageIn,
uint256 _maxSlippageOut,
uint256 _maxSingleInvest
) internal virtual {
// health.ychad.eth
// this is commented out because sometimes we have more than 1 bip of losses,
// and tests wont pass in those cases. Should be uncommented before deployment
// healthCheck = address(0xDDCea799fF1699e98EDF118e0629A974Df7DF012);
bptVault = IVault(_bptVault);
balancerPool = IBalancerPool(bptVault.token());
bytes32 _poolID = balancerPool.getPoolId();
balancerPoolID = _poolID;
(IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(_poolID);
uint8 _numTokens = uint8(tokens.length);
numTokens = _numTokens;
require(_numTokens > 0, "Empty Pool");
require(_numTokens <= MAX_TOKENS, "Exceeds max tokens");
assets = new IAsset[](numTokens);
uint8 _tokenIndex = type(uint8).max;
for (uint8 i = 0; i < _numTokens; i++) {
if (tokens[i] == want) {
_tokenIndex = i;
}
assets[i] = IAsset(address(tokens[i]));
}
// require(_tokenIndex != type(uint8).max, "token not supported in pool!");
tokenIndex = _tokenIndex;
maxSlippageIn = _maxSlippageIn;
maxSlippageOut = _maxSlippageOut;
maxSingleInvest = _maxSingleInvest;
want.safeApprove(address(balancerVault), type(uint256).max);
IERC20(address(balancerPool)).safeApprove(
address(bptVault),
type(uint256).max
);
withdrawProtection = true;
}
function initialize(
address _vault,
address _strategist,
address _rewards,
address _keeper,
address _bptVault,
uint256 _maxSlippageIn,
uint256 _maxSlippageOut,
uint256 _maxSingleInvest
) external virtual {
_initialize(_vault, _strategist, _rewards, _keeper);
_initializeStrat(
_bptVault,
_maxSlippageIn,
_maxSlippageOut,
_maxSingleInvest
);
}
function clone(
address _vault,
address _strategist,
address _rewards,
address _keeper,
address _bptVault,
uint256 _maxSlippageIn,
uint256 _maxSlippageOut,
uint256 _maxSingleInvest
) external returns (address newStrategy) {
require(isOriginal, "!clone");
bytes20 addressBytes = bytes20(address(this));
assembly {
// EIP-1167 bytecode
let clone_code := mload(0x40)
mstore(
clone_code,
0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
)
mstore(add(clone_code, 0x14), addressBytes)
mstore(
add(clone_code, 0x28),
0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
)
newStrategy := create(0, clone_code, 0x37)
}
BasicSingleSidedBalancer(newStrategy).initialize(
_vault,
_strategist,
_rewards,
_keeper,
_bptVault,
_maxSlippageIn,
_maxSlippageOut,
_maxSingleInvest
);
emit Cloned(newStrategy);
}
function extensionName() internal view override returns (string memory) {
// basic pool, no frills
return "BASIC";
}
function investWantIntoBalancerPool(uint256 _wantAmount) internal override {
uint256 _minBPTOut = (wantToBPT(_wantAmount) *
(MAX_BPS - maxSlippageIn)) / MAX_BPS;
uint256[] memory _maxAmountsIn = new uint256[](numTokens);
_maxAmountsIn[tokenIndex] = _wantAmount;
if (_wantAmount > 0) {
bytes memory _userData = abi.encode(
IBalancerVault.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,
_maxAmountsIn,
_minBPTOut
);
IBalancerVault.JoinPoolRequest memory _request = IBalancerVault
.JoinPoolRequest(assets, _maxAmountsIn, _userData, false);
balancerVault.joinPool(
balancerPoolID,
address(this),
address(this),
_request
);
}
}
function liquidateBPTsToWant(uint256 _bptAmount) internal override {
uint256[] memory _minAmountsOut = new uint256[](numTokens);
_minAmountsOut[tokenIndex] =
(bptToWant(_bptAmount) * (MAX_BPS - maxSlippageOut)) /
MAX_BPS;
bytes memory _userData = abi.encode(
IBalancerVault.ExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,
_bptAmount,
tokenIndex
);
IBalancerVault.ExitPoolRequest memory _request = IBalancerVault
.ExitPoolRequest(assets, _minAmountsOut, _userData, false);
balancerVault.exitPool(
balancerPoolID,
address(this),
payable(address(this)),
_request
);
}
}
// Phantom BPTs need to be swapped into. Since I don't trust ySwaps to swap principal,
// you need to pass in a swap route during deployment
contract PhantomSingleSidedBalancer is BaseSingleSidedBalancer {
using SafeERC20 for IERC20;
using Address for address;
// These three are set manually by strategists
bytes32[] public swapPathPoolIDs; // pool IDs of pools in your swap path, in the order of swapping.
IAsset[] public swapPathAssets; // these MUST be sorted numerically
uint256[] public swapPathAssetIndexes;
/*
* Example: let's say we wanted to single-side DAI into the boosted pool
* swapPathPoolIDs[0] would be the ID of the pool that contains regular DAI and bb_a_DAI
* swapPathPoolIDs[1] would be the ID of the pool that contains bb_a_DAI and bb_a_USD (the real BPT)
* If these were reversed, the strategy would try to swap DAI into the bb_a_DAI/bb_a_USD pool, which doesn't make any sense.
* assets would be DAI, bb_a_DAI, and bb_a_USD, sorted numerically (so not necessarily in that order, but let's assume that this is the case)
* swapPathAssetIndexes would be [0, 1, 2]
* if assets was [bb_a_DAI, bb_a_USD, DAI], swapPathAssetIndexes would be [2, 1, 0]
*/
// These are set automatically by the code
int256[] limits;
IBalancerVault.BatchSwapStep[] batchSwapSteps;
IBalancerVault.BatchSwapStep[] reverseBatchSwapSteps;
event Cloned(address indexed clone);
bool public isOriginal = true;
// Cloning & initialization code adapted from https://github.com/yearn/yearn-vaults/blob/43a0673ab89742388369bc0c9d1f321aa7ea73f6/contracts/BaseStrategy.sol#L866
constructor(
address _vault,
address _bptVault,
uint256 _maxSlippageIn,
uint256 _maxSlippageOut,
uint256 _maxSingleInvest,
bytes32[] memory _swapPathPoolIDs,
IAsset[] memory _swapPathAssets,
uint256[] memory _swapPathAssetIndexes
) BaseSingleSidedBalancer(_vault) {
_initializeStrat(
_bptVault,
_maxSlippageIn,
_maxSlippageOut,
_maxSingleInvest,
_swapPathPoolIDs,
_swapPathAssets,
_swapPathAssetIndexes
);
}
function _initializeStrat(
address _bptVault,
uint256 _maxSlippageIn,
uint256 _maxSlippageOut,
uint256 _maxSingleInvest,
bytes32[] memory _swapPathPoolIDs,
IAsset[] memory _swapPathAssets,
uint256[] memory _swapPathAssetIndexes
) internal virtual {
// health.ychad.eth
// this is commented out because sometimes we have more than 1 bip of losses,
// and tests wont pass in those cases. Should be uncommented before deployment
// healthCheck = address(0xDDCea799fF1699e98EDF118e0629A974Df7DF012);
bptVault = IVault(_bptVault);
balancerPool = IBalancerPool(bptVault.token());
bytes32 _poolID = balancerPool.getPoolId();
balancerPoolID = _poolID;
maxSlippageIn = _maxSlippageIn;
maxSlippageOut = _maxSlippageOut;
maxSingleInvest = _maxSingleInvest;
want.safeApprove(address(balancerVault), type(uint256).max);
IERC20(address(balancerPool)).safeApprove(
address(bptVault),
type(uint256).max
);
swapPathPoolIDs = _swapPathPoolIDs;
swapPathAssets = _swapPathAssets;
swapPathAssetIndexes = _swapPathAssetIndexes;
// we need to create one swap path for swapping want -> BPT and one for swapping BPT -> want
// example of swap steps here: https://github.com/charlesndalton/StrategyBalancerTemplate/blob/df947fbb2f4b973d46b820001e476dfbe27c0826/tests/test_yswap.py#L79-L104
uint256 _numberOfPools = swapPathPoolIDs.length;
for (uint8 i = 0; i < _numberOfPools; i++) {
batchSwapSteps.push(
IBalancerVault.BatchSwapStep(
swapPathPoolIDs[i], // poolId
swapPathAssetIndexes[i], // assetInIndex
swapPathAssetIndexes[i + 1], // assetOutIndex
0, // amount (PLACEHOLDER)
abi.encode(0) // userData
)
);
reverseBatchSwapSteps.push(
IBalancerVault.BatchSwapStep(
swapPathPoolIDs[_numberOfPools - 1 - i],
swapPathAssetIndexes[_numberOfPools - i],
swapPathAssetIndexes[_numberOfPools - 1 - i],
0,
abi.encode(0)
)
);
limits.push(2**200);
}
limits.push(2**200); // always will be 1 more asset than pool, this isn't the cleanest way to do it but saves some gas
withdrawProtection = true;
}
function initialize(
address _vault,
address _strategist,
address _rewards,
address _keeper,
address _bptVault,
uint256 _maxSlippageIn,
uint256 _maxSlippageOut,
uint256 _maxSingleInvest,
bytes32[] memory _swapPathPoolIDs,
IAsset[] memory _swapPathAssets,
uint256[] memory _swapPathAssetIndexes
) external virtual {
_initialize(_vault, _strategist, _rewards, _keeper);
_initializeStrat(
_bptVault,
_maxSlippageIn,
_maxSlippageOut,
_maxSingleInvest,
_swapPathPoolIDs,
_swapPathAssets,
_swapPathAssetIndexes
);
}
function clone(
address _vault,
address _strategist,
address _rewards,
address _keeper,
address _bptVault,
uint256 _maxSlippageIn,
uint256 _maxSlippageOut,
uint256 _maxSingleInvest,
bytes32[] memory _swapPathPoolIDs,
IAsset[] memory _swapPathAssets,
uint256[] memory _swapPathAssetIndexes
) external returns (address newStrategy) {
require(isOriginal, "!clone");
bytes20 addressBytes = bytes20(address(this));
assembly {
// EIP-1167 bytecode
let clone_code := mload(0x40)
mstore(
clone_code,
0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
)
mstore(add(clone_code, 0x14), addressBytes)
mstore(
add(clone_code, 0x28),
0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
)
newStrategy := create(0, clone_code, 0x37)
}
PhantomSingleSidedBalancer(newStrategy).initialize(
_vault,
_strategist,
_rewards,
_keeper,
_bptVault,
_maxSlippageIn,
_maxSlippageOut,
_maxSingleInvest,
_swapPathPoolIDs,
_swapPathAssets,
_swapPathAssetIndexes
);
emit Cloned(newStrategy);
}
function extensionName() internal view override returns (string memory) {
return "PHANTOM";
}
function investWantIntoBalancerPool(uint256 _wantAmount) internal override {
// uint256 _minBPTOut = (wantToBPT(_wantAmount) *
// (MAX_BPS - maxSlippageIn)) / MAX_BPS;
batchSwapSteps[0].amount = _wantAmount;
IBalancerVault.FundManagement memory _funds = IBalancerVault
.FundManagement(
address(this), // sender
false, // fromInternalBalance
payable(address(this)), // recipient
false // toInternalBalance
);
balancerVault.batchSwap(
IBalancerVault.SwapKind.GIVEN_IN,
batchSwapSteps,
swapPathAssets,
_funds,
limits,
block.timestamp
);
}
function liquidateBPTsToWant(uint256 _bptAmount) internal override {
reverseBatchSwapSteps[0].amount = _bptAmount;
IBalancerVault.FundManagement memory _funds = IBalancerVault
.FundManagement(
address(this), // sender
false, // fromInternalBalance
payable(address(this)), // recipient
false // toInternalBalance
);
balancerVault.batchSwap(
IBalancerVault.SwapKind.GIVEN_IN,
reverseBatchSwapSteps,
swapPathAssets,
_funds,
limits,
block.timestamp
);
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_bptVault","type":"address"},{"internalType":"uint256","name":"_maxSlippageIn","type":"uint256"},{"internalType":"uint256","name":"_maxSlippageOut","type":"uint256"},{"internalType":"uint256","name":"_maxSingleInvest","type":"uint256"},{"internalType":"bytes32[]","name":"_swapPathPoolIDs","type":"bytes32[]"},{"internalType":"contract IAsset[]","name":"_swapPathAssets","type":"address[]"},{"internalType":"uint256[]","name":"_swapPathAssetIndexes","type":"uint256[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"}],"name":"Cloned","type":"event"},{"anonymous":false,"inputs":[],"name":"EmergencyExitEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"profit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"loss","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtPayment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtOutstanding","type":"uint256"}],"name":"Harvested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"","type":"bool"}],"name":"SetDoHealthCheck","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"}],"name":"SetHealthCheck","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"debtThreshold","type":"uint256"}],"name":"UpdatedDebtThreshold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newKeeper","type":"address"}],"name":"UpdatedKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delay","type":"uint256"}],"name":"UpdatedMaxReportDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"metadataURI","type":"string"}],"name":"UpdatedMetadataURI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delay","type":"uint256"}],"name":"UpdatedMinReportDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"profitFactor","type":"uint256"}],"name":"UpdatedProfitFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"rewards","type":"address"}],"name":"UpdatedRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newStrategist","type":"address"}],"name":"UpdatedStrategist","type":"event"},{"inputs":[],"name":"apiVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"balancerPool","outputs":[{"internalType":"contract IBalancerPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balancerPoolID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_bptAmount","type":"uint256"}],"name":"bptToWant","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bptVault","outputs":[{"internalType":"contract IVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_strategist","type":"address"},{"internalType":"address","name":"_rewards","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_bptVault","type":"address"},{"internalType":"uint256","name":"_maxSlippageIn","type":"uint256"},{"internalType":"uint256","name":"_maxSlippageOut","type":"uint256"},{"internalType":"uint256","name":"_maxSingleInvest","type":"uint256"},{"internalType":"bytes32[]","name":"_swapPathPoolIDs","type":"bytes32[]"},{"internalType":"contract IAsset[]","name":"_swapPathAssets","type":"address[]"},{"internalType":"uint256[]","name":"_swapPathAssetIndexes","type":"uint256[]"}],"name":"clone","outputs":[{"internalType":"address","name":"newStrategy","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"debtThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delegatedAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"doHealthCheck","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyExit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"estimatedTotalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amtInWei","type":"uint256"}],"name":"ethToWant","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"callCostInWei","type":"uint256"}],"name":"harvestTrigger","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"healthCheck","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_strategist","type":"address"},{"internalType":"address","name":"_rewards","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_bptVault","type":"address"},{"internalType":"uint256","name":"_maxSlippageIn","type":"uint256"},{"internalType":"uint256","name":"_maxSlippageOut","type":"uint256"},{"internalType":"uint256","name":"_maxSingleInvest","type":"uint256"},{"internalType":"bytes32[]","name":"_swapPathPoolIDs","type":"bytes32[]"},{"internalType":"contract IAsset[]","name":"_swapPathAssets","type":"address[]"},{"internalType":"uint256[]","name":"_swapPathAssetIndexes","type":"uint256[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOriginal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastDepositTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxReportDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSingleInvest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSlippageIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSlippageOut","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadataURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newStrategy","type":"address"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minReportDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numTokens","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolTokensInYVault","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"profitFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewards","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debtThreshold","type":"uint256"}],"name":"setDebtThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_doHealthCheck","type":"bool"}],"name":"setDoHealthCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setEmergencyExit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_healthCheck","type":"address"}],"name":"setHealthCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keeper","type":"address"}],"name":"setKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_delay","type":"uint256"}],"name":"setMaxReportDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_metadataURI","type":"string"}],"name":"setMetadataURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_delay","type":"uint256"}],"name":"setMinReportDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_profitFactor","type":"uint256"}],"name":"setProfitFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewards","type":"address"}],"name":"setRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategist","type":"address"}],"name":"setStrategist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strategist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"swapPathAssetIndexes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"swapPathAssets","outputs":[{"internalType":"contract IAsset","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"swapPathPoolIDs","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"callCostInWei","type":"uint256"}],"name":"tendTrigger","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenIndex","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxSingleInvest","type":"uint256"}],"name":"updateMaxSingleInvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxSlippageIn","type":"uint256"}],"name":"updateMaxSlippageIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxSlippageOut","type":"uint256"}],"name":"updateMaxSlippageOut","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_withdrawProtection","type":"bool"}],"name":"updateWithdrawProtection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract VaultAPI","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"want","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_wantAmount","type":"uint256"}],"name":"wantToBPT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountNeeded","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"_loss","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawProtection","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6080604052601b805460ff191660011790553480156200001e57600080fd5b506040516200629238038062006292833981016040819052620000419162000d3e565b8780620000518133808062000073565b506200006590508787878787878762000257565b5050505050505050620010cb565b6006546001600160a01b031615620000d25760405162461bcd60e51b815260206004820152601c60248201527f537472617465677920616c726561647920696e697469616c697a65640000000060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b03861690811790915560408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa1580156200012c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000152919062000e14565b600680546001600160a01b0319166001600160a01b039290921691821790556200018c90856000196200074c602090811b62002aa117901c565b600380546001600160a01b038581166001600160a01b03199283161790925560048054858416908316811782556005805486861694169390931790925560006007819055620151806008556064600955600a5560025460405163095ea7b360e01b815291820192909252600019602482015291169063095ea7b3906044016020604051808303816000875af11580156200022a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000250919062000e34565b5050505050565b600d80546001600160a01b0319166001600160a01b03891690811790915560408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa158015620002b1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002d7919062000e14565b600e80546001600160a01b0319166001600160a01b039290921691821790556040805163038fff2d60e41b81529051600092916338fff2d09160048083019260209291908290030181865afa15801562000335573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200035b919062000e58565b6010819055601188905560128790556013869055600654909150620003ad906001600160a01b031673ba12222222228d8ba445958a75a0704d566bf2c86000196200074c602090811b62002aa117901c565b600d54600e54620003da916001600160a01b0391821691166000196200074c602090811b62002aa117901c565b8351620003ef90601590602087019062000b05565b5082516200040590601690602086019062000b55565b5081516200041b90601790602085019062000b05565b5060155460005b818160ff161015620006f85760196040518060a0016040528060158460ff168154811062000454576200045462000e72565b9060005260206000200154815260200160178460ff16815481106200047d576200047d62000e72565b9060005260206000200154815260200160178460016200049e919062000e9e565b60ff1681548110620004b457620004b462000e72565b90600052602060002001548152602001600081526020016000604051602001620004e7919060ff91909116815260200190565b60408051601f198184030181529181529152825460018181018555600094855260209485902084516005909302019182559383015193810193909355810151600283015560608101516003830155608081015190919060048201906200054e908262000f4e565b505050601a6040518060a0016040528060158460ff166001876200057391906200101a565b6200057f91906200101a565b8154811062000592576200059262000e72565b9060005260206000200154815260200160178460ff1686620005b591906200101a565b81548110620005c857620005c862000e72565b9060005260206000200154815260200160178460ff16600187620005ed91906200101a565b620005f991906200101a565b815481106200060c576200060c62000e72565b906000526020600020015481526020016000815260200160006040516020016200063f919060ff91909116815260200190565b60408051601f19818403018152918152915282546001818101855560009485526020948590208451600590930201918255938301519381019390935581015160028301556060810151600383015560808101519091906004820190620006a6908262000f4e565b505060188054600181018255600091909152600160c81b7fb13d2d76d1f4b7be834882e410b3e3a8afaf69f83600ae24db354391d2378d2e909101555080620006ef8162001030565b91505062000422565b50506018805460018181018355600092909252600160c81b7fb13d2d76d1f4b7be834882e410b3e3a8afaf69f83600ae24db354391d2378d2e909101556014805460ff191690911790555050505050505050565b801580620007ca5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015620007a2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007c8919062000e58565b155b6200083e5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401620000c9565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b17909152620008969185916200089b16565b505050565b6000620008f7826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166200097960201b62002bee179092919060201c565b80519091501562000896578080602001905181019062000918919062000e34565b620008965760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401620000c9565b60606200098a848460008562000994565b90505b9392505050565b606082471015620009f75760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401620000c9565b843b62000a475760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620000c9565b600080866001600160a01b0316858760405162000a65919062001078565b60006040518083038185875af1925050503d806000811462000aa4576040519150601f19603f3d011682016040523d82523d6000602084013e62000aa9565b606091505b50909250905062000abc82828662000ac7565b979650505050505050565b6060831562000ad85750816200098d565b82511562000ae95782518084602001fd5b8160405162461bcd60e51b8152600401620000c9919062001096565b82805482825590600052602060002090810192821562000b43579160200282015b8281111562000b4357825182559160200191906001019062000b26565b5062000b5192915062000bad565b5090565b82805482825590600052602060002090810192821562000b43579160200282015b8281111562000b4357825182546001600160a01b0319166001600160a01b0390911617825560209092019160019091019062000b76565b5b8082111562000b51576000815560010162000bae565b6001600160a01b038116811462000bda57600080fd5b50565b805162000bea8162000bc4565b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171562000c305762000c3062000bef565b604052919050565b60006001600160401b0382111562000c545762000c5462000bef565b5060051b60200190565b600082601f83011262000c7057600080fd5b8151602062000c8962000c838362000c38565b62000c05565b82815260059290921b8401810191818101908684111562000ca957600080fd5b8286015b8481101562000cc6578051835291830191830162000cad565b509695505050505050565b600082601f83011262000ce357600080fd5b8151602062000cf662000c838362000c38565b82815260059290921b8401810191818101908684111562000d1657600080fd5b8286015b8481101562000cc657805162000d308162000bc4565b835291830191830162000d1a565b600080600080600080600080610100898b03121562000d5c57600080fd5b62000d678962000bdd565b975062000d7760208a0162000bdd565b60408a015160608b015160808c015160a08d0151939a50919850965094506001600160401b038082111562000dab57600080fd5b62000db98c838d0162000c5e565b945060c08b015191508082111562000dd057600080fd5b62000dde8c838d0162000cd1565b935060e08b015191508082111562000df557600080fd5b5062000e048b828c0162000c5e565b9150509295985092959890939650565b60006020828403121562000e2757600080fd5b81516200098d8162000bc4565b60006020828403121562000e4757600080fd5b815180151581146200098d57600080fd5b60006020828403121562000e6b57600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60ff818116838216019081111562000eba5762000eba62000e88565b92915050565b600181811c9082168062000ed557607f821691505b60208210810362000ef657634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200089657600081815260208120601f850160051c8101602086101562000f255750805b601f850160051c820191505b8181101562000f465782815560010162000f31565b505050505050565b81516001600160401b0381111562000f6a5762000f6a62000bef565b62000f828162000f7b845462000ec0565b8462000efc565b602080601f83116001811462000fba576000841562000fa15750858301515b600019600386901b1c1916600185901b17855562000f46565b600085815260208120601f198616915b8281101562000feb5788860151825594840194600190910190840162000fca565b50858210156200100a5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8181038181111562000eba5762000eba62000e88565b600060ff821660ff810362001049576200104962000e88565b60010192915050565b60005b838110156200106f57818101518382015260200162001055565b50506000910152565b600082516200108c81846020870162001052565b9190910192915050565b6020815260008251806020840152620010b781604085016020870162001052565b601f01601f19169190910160400192915050565b6151b780620010db6000396000f3fe608060405234801561001057600080fd5b50600436106103a45760003560e01c8063776da470116101e9578063b252720b1161010f578063e3e1d552116100ad578063f017c92f1161007c578063f017c92f14610778578063fbfa77cf1461078b578063fcf2d0ad1461079e578063fe048ea3146107a657600080fd5b8063e3e1d55214610742578063ec38a8621461074a578063ed882c2b1461075d578063efbb5cb01461077057600080fd5b8063ce5494bb116100e9578063ce5494bb146106f5578063d0718afe14610708578063d55f92731461071b578063e02aa7ba1461072f57600080fd5b8063b252720b146106c1578063bd28f351146106d9578063c7b9d530146106e257600080fd5b806391397ab4116101875780639ec5a894116101565780639ec5a8941461067b578063a430735b1461068e578063ac00ff261461069b578063aced1661146106ae57600080fd5b806391397ab414610639578063934194ab1461064c578063939d1d271461065f57806395e80c501461067257600080fd5b80638cdfe166116101c35780638cdfe166146105f95780638e499bcf146106025780638e6350e2146106285780638ebff3781461063057600080fd5b8063776da470146105c5578063780022a0146105d8578063899552fb146105e657600080fd5b806335640a77116102ce57806360700ce71161026c5780636f392ce71161023b5780636f392ce7146105895780637085d5ed14610596578063748747e61461059f578063750521f5146105b257600080fd5b806360700ce71461054c578063650d18801461055f5780636718835f146105735780636c4a3c2a1461058057600080fd5b8063440368a3116102a8578063440368a31461051c5780634641257d146105245780635641ec031461052c5780635b916a401461053957600080fd5b806335640a77146104e357806339a172a8146104f65780633f308fa31461050957600080fd5b80631d12f28b1161034657806322f3e2d41161031557806322f3e2d41461048e57806325829410146104a657806328b7ccf7146104c75780632e1a7d4d146104d057600080fd5b80631d12f28b1461043e5780631f1fcd51146104475780631fe4a6861461047257806322ecf8231461048557600080fd5b80630f969b87116103825780630f969b87146103e457806311bc8245146103f757806313328f1d1461040a578063143b00411461042b57600080fd5b806301681a62146103a957806303ee438c146103be57806306fdde03146103dc575b600080fd5b6103bc6103b736600461442c565b6107b9565b005b6103c6610998565b6040516103d3919061446d565b60405180910390f35b6103c6610a26565b6103bc6103f23660046144a0565b610b57565b6103bc61040536600461442c565b610be0565b61041d6104183660046144a0565b610d0e565b6040519081526020016103d3565b6103bc6104393660046144c7565b610dcd565b61041d600a5481565b60065461045a906001600160a01b031681565b6040516001600160a01b0390911681526020016103d3565b60035461045a906001600160a01b031681565b61041d60105481565b610496610eaa565b60405190151581526020016103d3565b604080518082019091526005815264302e342e3360d81b60208201526103c6565b61041d60085481565b61041d6104de3660046144a0565b610f38565b61041d6104f13660046144a0565b610fac565b6103bc6105043660046144a0565b610fcd565b6103bc6105173660046144a0565b61104f565b6103bc61111e565b6103bc61131a565b600b546104969060ff1681565b600d5461045a906001600160a01b031681565b6103bc61055a3660046144a0565b611808565b61049661056d3660046144a0565b50600090565b6001546104969060ff1681565b61041d60125481565b601b546104969060ff1681565b61041d60135481565b6103bc6105ad36600461442c565b6118d7565b6103bc6105c03660046144e4565b611985565b600e5461045a906001600160a01b031681565b61041d61056d3660046144a0565b6103bc6105f43660046144a0565b611a1d565b61041d60095481565b600e5461061690600160a01b900460ff1681565b60405160ff90911681526020016103d3565b61041d611aec565b61041d60115481565b6103bc6106473660046144a0565b611b63565b61045a61065a3660046146ba565b611be5565b6103bc61066d3660046146ba565b611d23565b61041d60075481565b60045461045a906001600160a01b031681565b6014546104969060ff1681565b6103bc6106a93660046144c7565b611d4b565b60055461045a906001600160a01b031681565b60015461045a9061010090046001600160a01b031681565b61041d600c5481565b6103bc6106f036600461442c565b611e5d565b6103bc61070336600461442c565b611f0b565b61041d6107163660046144a0565b612036565b600e5461061690600160a81b900460ff1681565b61041d61073d3660046144a0565b6120ed565b61041d6120fd565b6103bc61075836600461442c565b61230b565b61049661076b3660046144a0565b6124a9565b61041d6126e6565b6103bc6107863660046144a0565b6127ee565b60025461045a906001600160a01b031681565b6103bc612870565b61045a6107b43660046144a0565b612a77565b6107c1612c05565b6001600160a01b0316336001600160a01b0316146107fa5760405162461bcd60e51b81526004016107f1906147bd565b60405180910390fd5b6006546001600160a01b03908116908216036108405760405162461bcd60e51b8152602060048201526005602482015264085dd85b9d60da1b60448201526064016107f1565b6002546001600160a01b03908116908216036108885760405162461bcd60e51b81526020600482015260076024820152662173686172657360c81b60448201526064016107f1565b606060005b815181101561090f578181815181106108a8576108a86147e2565b60200260200101516001600160a01b0316836001600160a01b0316036108fd5760405162461bcd60e51b815260206004820152600a602482015269085c1c9bdd1958dd195960b21b60448201526064016107f1565b806109078161480e565b91505061088d565b5061099461091b612c05565b6040516370a0823160e01b81523060048201526001600160a01b038516906370a0823190602401602060405180830381865afa15801561095f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109839190614827565b6001600160a01b0385169190612c73565b5050565b600080546109a590614840565b80601f01602080910402602001604051908101604052809291908181526020018280546109d190614840565b8015610a1e5780601f106109f357610100808354040283529160200191610a1e565b820191906000526020600020905b815481529060010190602001808311610a0157829003601f168201915b505050505081565b600654604080516395d89b4160e01b815290516060926001600160a01b0316916395d89b419160048083019260009291908290030181865afa158015610a70573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610a989190810190614874565b6040805180820190915260078152665048414e544f4d60c81b6020820152600e60009054906101000a90046001600160a01b03166001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015610b09573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b319190810190614874565b604051602001610b4393929190614908565b604051602081830303815290604052905090565b6003546001600160a01b0316331480610b885750610b73612c05565b6001600160a01b0316336001600160a01b0316145b610ba45760405162461bcd60e51b81526004016107f1906147bd565b600a8190556040518181527fa68ba126373d04c004c5748c300c9fca12bd444b3d4332e261f3bd2bac4a8600906020015b60405180910390a150565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c579190614972565b6001600160a01b0316336001600160a01b03161480610c8e5750610c79612c05565b6001600160a01b0316336001600160a01b0316145b610caa5760405162461bcd60e51b81526004016107f1906147bd565b6040516001600160a01b03821681527fc8db9c35f716b87af1fbb83f03c78646061931269301fd7ba6dcf189b4cdc2fc9060200160405180910390a1600180546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b600080670de0b6b3a7640000600e60009054906101000a90046001600160a01b03166001600160a01b031663679aefce6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d919190614827565b610d9b908561498f565b610da591906149ae565b600e54600654919250610dc69183916001600160a01b039081169116612ca3565b9392505050565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e449190614972565b6001600160a01b0316336001600160a01b03161480610e7b5750610e66612c05565b6001600160a01b0316336001600160a01b0316145b610e975760405162461bcd60e51b81526004016107f1906147bd565b6014805460ff1916911515919091179055565b6002546040516339ebf82360e01b815230600482015260009182916001600160a01b03909116906339ebf8239060240161012060405180830381865afa158015610ef8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1c91906149d0565b604001511180610f3357506000610f316126e6565b115b905090565b6002546000906001600160a01b03163314610f7e5760405162461bcd60e51b8152602060048201526006602482015265085d985d5b1d60d21b60448201526064016107f1565b6000610f8983612dca565b600654909350909150610fa6906001600160a01b03163383612c73565b50919050565b60178181548110610fbc57600080fd5b600091825260209091200154905081565b6003546001600160a01b0316331480610ffe5750610fe9612c05565b6001600160a01b0316336001600160a01b0316145b61101a5760405162461bcd60e51b81526004016107f1906147bd565b60078190556040518181527fbb2c369a0355a34b02ab5fce0643150c87e1c8dfe7c918d465591879f57948b190602001610bd5565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c69190614972565b6001600160a01b0316336001600160a01b031614806110fd57506110e8612c05565b6001600160a01b0316336001600160a01b0316145b6111195760405162461bcd60e51b81526004016107f1906147bd565b601355565b6005546001600160a01b031633148061114157506003546001600160a01b031633145b80611164575061114f612c05565b6001600160a01b0316336001600160a01b0316145b806111f65750600260009054906101000a90046001600160a01b03166001600160a01b031663452a93206040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e19190614972565b6001600160a01b0316336001600160a01b0316145b806112885750600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa15801561124f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112739190614972565b6001600160a01b0316336001600160a01b0316145b6112a45760405162461bcd60e51b81526004016107f1906147bd565b6002546040805163bf3759b560e01b81529051611318926001600160a01b03169163bf3759b59160048083019260209291908290030181865afa1580156112ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113139190614827565b612e88565b565b6005546001600160a01b031633148061133d57506003546001600160a01b031633145b80611360575061134b612c05565b6001600160a01b0316336001600160a01b0316145b806113f25750600260009054906101000a90046001600160a01b03166001600160a01b031663452a93206040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113dd9190614972565b6001600160a01b0316336001600160a01b0316145b806114845750600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa15801561144b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146f9190614972565b6001600160a01b0316336001600160a01b0316145b6114a05760405162461bcd60e51b81526004016107f1906147bd565b6000806000600260009054906101000a90046001600160a01b03166001600160a01b031663bf3759b56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151c9190614827565b600b5490915060009060ff1615611579576000611537612fa7565b9050828110156115525761154b8184614a4b565b9350611567565b82811115611567576115648382614a4b565b94505b6115718484614a4b565b91505061158a565b6115828261315d565b919550935090505b6002546040516339ebf82360e01b81523060048201526000916001600160a01b0316906339ebf8239060240161012060405180830381865afa1580156115d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f891906149d0565b60c001516002546040516328766ebf60e21b81526004810188905260248101879052604481018590529192506001600160a01b03169063a1d9bafc906064016020604051808303816000875af1158015611656573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167a9190614827565b925061168583612e88565b60015460ff1680156116a6575060015461010090046001600160a01b031615155b156117785760015460405163c70fa00b60e01b815260048101879052602481018690526044810184905260648101859052608481018390526101009091046001600160a01b03169063c70fa00b9060a401602060405180830381865afa158015611714573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117389190614a5e565b6117735760405162461bcd60e51b815260206004820152600c60248201526b216865616c7468636865636b60a01b60448201526064016107f1565b6117b9565b604051600181527ff769f6bf659bbbdabf212d830720ce893eedc57f25ebb8e44edf5b300618a35b9060200160405180910390a16001805460ff1916811790555b6040805186815260208101869052908101839052606081018490527f4c0f499ffe6befa0ca7c826b0916cf87bea98de658013e76938489368d60d5099060800160405180910390a15050505050565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa15801561185b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187f9190614972565b6001600160a01b0316336001600160a01b031614806118b657506118a1612c05565b6001600160a01b0316336001600160a01b0316145b6118d25760405162461bcd60e51b81526004016107f1906147bd565b601155565b6003546001600160a01b031633148061190857506118f3612c05565b6001600160a01b0316336001600160a01b0316145b6119245760405162461bcd60e51b81526004016107f1906147bd565b6001600160a01b03811661193757600080fd5b600580546001600160a01b0319166001600160a01b0383169081179091556040519081527f2f202ddb4a2e345f6323ed90f8fc8559d770a7abbbeee84dde8aca3351fe715490602001610bd5565b6003546001600160a01b03163314806119b657506119a1612c05565b6001600160a01b0316336001600160a01b0316145b6119d25760405162461bcd60e51b81526004016107f1906147bd565b60006119df828483614ac9565b507f300e67d5a415b6d015a471d9c7b95dd58f3e8290af965e84e0f845de2996dda68282604051611a11929190614b89565b60405180910390a15050565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a949190614972565b6001600160a01b0316336001600160a01b03161480611acb5750611ab6612c05565b6001600160a01b0316336001600160a01b0316145b611ae75760405162461bcd60e51b81526004016107f1906147bd565b601255565b6002546040516339ebf82360e01b81523060048201526000916001600160a01b0316906339ebf8239060240161012060405180830381865afa158015611b36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5a91906149d0565b60c00151905090565b6003546001600160a01b0316331480611b945750611b7f612c05565b6001600160a01b0316336001600160a01b0316145b611bb05760405162461bcd60e51b81526004016107f1906147bd565b60098190556040518181527fd94596337df4c2f0f44d30a7fc5db1c7bb60d9aca4185ed77c6fd96eb45ec29890602001610bd5565b601b5460009060ff16611c235760405162461bcd60e51b815260206004820152600660248201526521636c6f6e6560d01b60448201526064016107f1565b604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81523060601b601482018190526e5af43d82803e903d91602b57fd5bf360881b6028830152906037816000f0925050816001600160a01b031663939d1d278e8e8e8e8e8e8e8e8e8e8e6040518c63ffffffff1660e01b8152600401611cad9b9a99989796959493929190614bf3565b600060405180830381600087803b158015611cc757600080fd5b505af1158015611cdb573d6000803e3d6000fd5b50506040516001600160a01b03851692507f783540fb4221a3238720dc7038937d0d79982bcf895274aa6ad179f82cf0d53c9150600090a2509b9a5050505050505050505050565b611d2f8b8b8b8b613391565b611d3e8787878787878761355b565b5050505050505050505050565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dc29190614972565b6001600160a01b0316336001600160a01b03161480611df95750611de4612c05565b6001600160a01b0316336001600160a01b0316145b611e155760405162461bcd60e51b81526004016107f1906147bd565b60405181151581527ff769f6bf659bbbdabf212d830720ce893eedc57f25ebb8e44edf5b300618a35b9060200160405180910390a16001805460ff1916911515919091179055565b6003546001600160a01b0316331480611e8e5750611e79612c05565b6001600160a01b0316336001600160a01b0316145b611eaa5760405162461bcd60e51b81526004016107f1906147bd565b6001600160a01b038116611ebd57600080fd5b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527f352ececae6d7d1e6d26bcf2c549dfd55be1637e9b22dc0cf3b71ddb36097a6b490602001610bd5565b6002546001600160a01b03163314611f2257600080fd5b6002546040805163fbfa77cf60e01b815290516001600160a01b039283169284169163fbfa77cf9160048083019260209291908290030181865afa158015611f6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f929190614972565b6001600160a01b031614611fa557600080fd5b611fae81613a00565b6006546040516370a0823160e01b81523060048201526120339183916001600160a01b03909116906370a0823190602401602060405180830381865afa158015611ffc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120209190614827565b6006546001600160a01b03169190612c73565b50565b600080600e60009054906101000a90046001600160a01b03166001600160a01b031663679aefce6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561208c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120b09190614827565b6120c284670de0b6b3a764000061498f565b6120cc91906149ae565b600654600e54919250610dc69183916001600160a01b039081169116612ca3565b60158181548110610fbc57600080fd5b600d546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a0823190602401602060405180830381865afa15801561214a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216e9190614827565b9050600d60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121e79190614827565b6000036121f657600091505090565b600d5460408051634ca9858360e11b815290516000926001600160a01b0316916399530b069160048083019260209291908290030181865afa158015612240573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122649190614827565b90506000600e60009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122df9190614cad565b60ff1690506122ef81600a614db4565b6122f9838561498f565b61230391906149ae565b935050505090565b6003546001600160a01b031633146123535760405162461bcd60e51b815260206004820152600b60248201526a085cdd1c985d1959da5cdd60aa1b60448201526064016107f1565b6001600160a01b03811661236657600080fd5b6002546004805460405163095ea7b360e01b81526001600160a01b0391821692810192909252600060248301529091169063095ea7b3906044016020604051808303816000875af11580156123bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e39190614a5e565b50600480546001600160a01b0319166001600160a01b03838116918217835560025460405163095ea7b360e01b8152938401929092526000196024840152169063095ea7b3906044016020604051808303816000875af115801561244b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061246f9190614a5e565b506040516001600160a01b03821681527fafbb66abf8f3b719799940473a4052a3717cdd8e40fb6c8a3faadab316b1a06990602001610bd5565b600080806002546040516339ebf82360e01b81523060048201529192506000916001600160a01b03909116906339ebf8239060240161012060405180830381865afa1580156124fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252091906149d0565b90508060200151600003612538575060009392505050565b60075460a082015161254a9042614a4b565b101561255a575060009392505050565b60085460a082015161256c9042614a4b565b1061257b575060019392505050565b6002546040805163bf3759b560e01b815290516000926001600160a01b03169163bf3759b59160048083019260209291908290030181865afa1580156125c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e99190614827565b9050600a5481111561260057506001949350505050565b600061260a6126e6565b90508260c00151600a548261261f9190614dc0565b10156126315750600195945050505050565b60008360c001518211156126515760c084015161264e9083614a4b565b90505b6002546040805163112c1f9b60e01b815290516000926001600160a01b03169163112c1f9b9160048083019260209291908290030181865afa15801561269b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126bf9190614827565b90506126cb8282614dc0565b866009546126d9919061498f565b1098975050505050505050565b6000806126f16120fd565b600e546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015612739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275d9190614827565b6127679190614dc0565b905061277281610d0e565b6006546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156127ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127de9190614827565b6127e89190614dc0565b91505090565b6003546001600160a01b031633148061281f575061280a612c05565b6001600160a01b0316336001600160a01b0316145b61283b5760405162461bcd60e51b81526004016107f1906147bd565b60088190556040518181527f5430e11864ad7aa9775b07d12657fe52df9aa2ba734355bd8ef8747be2c800c590602001610bd5565b6003546001600160a01b03163314806128a1575061288c612c05565b6001600160a01b0316336001600160a01b0316145b806129335750600260009054906101000a90046001600160a01b03166001600160a01b031663452a93206040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291e9190614972565b6001600160a01b0316336001600160a01b0316145b806129c55750600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa15801561298c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129b09190614972565b6001600160a01b0316336001600160a01b0316145b6129e15760405162461bcd60e51b81526004016107f1906147bd565b600b805460ff191660011790556002546040805163507257cd60e11b815290516001600160a01b039092169163a0e4af9a9160048082019260009290919082900301818387803b158015612a3457600080fd5b505af1158015612a48573d6000803e3d6000fd5b50506040517f97e963041e952738788b9d4871d854d282065b8f90a464928d6528f2e9a4fd0b925060009150a1565b60168181548110612a8757600080fd5b6000918252602090912001546001600160a01b0316905081565b801580612b1b5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612af5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b199190614827565b155b612b865760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016107f1565b6040516001600160a01b038316602482015260448101829052612be990849063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613ae5565b505050565b6060612bfd8484600085613bb7565b949350505050565b60025460408051635aa6e67560e01b815290516000926001600160a01b031691635aa6e6759160048083019260209291908290030181865afa158015612c4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f339190614972565b6040516001600160a01b038316602482015260448101829052612be990849063a9059cbb60e01b90606401612bb2565b600080836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ce4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d089190614cad565b60ff1690506000836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d719190614cad565b60ff169050818111612da157612d878183614a4b565b612d9290600a614db4565b612d9c90876149ae565b612dc0565b612dab8282614a4b565b612db690600a614db4565b612dc0908761498f565b9695505050505050565b6006546040516370a0823160e01b8152306004820152600091829182916001600160a01b0316906370a0823190602401602060405180830381865afa158015612e17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e3b9190614827565b905083811015612e61576000612e518286614a4b565b9050612e5c81613cdf565b509350505b612e7484612e6f8386614dc0565b614194565b9250612e808385614a4b565b915050915091565b6006546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015612ed1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef59190614827565b905081811115610994576000612f0b8383614a4b565b9050612f1981601354614194565b9050612f24816141ae565b600d60009054906101000a90046001600160a01b03166001600160a01b031663d0e30db06040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612f79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f9d9190614827565b505042600c555050565b600d546040516370a0823160e01b81523060048201526000916001600160a01b031690632e1a7d4d9082906370a0823190602401602060405180830381865afa158015612ff8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061301c9190614827565b6040518263ffffffff1660e01b815260040161303a91815260200190565b6020604051808303816000875af1158015613059573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307d9190614827565b50600e546040516370a0823160e01b81523060048201526130f1916001600160a01b0316906370a0823190602401602060405180830381865afa1580156130c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130ec9190614827565b614287565b6006546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015613139573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f339190614827565b6002546040516339ebf82360e01b81523060048201526000918291849183916001600160a01b03909116906339ebf8239060240161012060405180830381865afa1580156131af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131d391906149d0565b60c00151905060006131e36126e6565b9050818111156131fe576131f78282614a4b565b945061320b565b6132088183614a4b565b93505b60006132178685614dc0565b6006546040516370a0823160e01b81523060048201529192506000916001600160a01b03909116906370a0823190602401602060405180830381865afa158015613265573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132899190614827565b90508082111561338657600061329f8284614a4b565b905060006132ac82613cdf565b915050888110156132c8576132c1818a614a4b565b98506132e3565b6132d28982614a4b565b6132dc9089614dc0565b9750600098505b6006546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa15801561332b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061334f9190614827565b9250888310156133655782985060009650613383565b61336f8988614dc0565b831015613383576133808984614a4b565b96505b50505b505050509193909250565b6006546001600160a01b0316156133ea5760405162461bcd60e51b815260206004820152601c60248201527f537472617465677920616c726561647920696e697469616c697a65640000000060448201526064016107f1565b600280546001600160a01b0319166001600160a01b03861690811790915560408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa158015613443573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134679190614972565b600680546001600160a01b0319166001600160a01b039290921691821790556134939085600019612aa1565b600380546001600160a01b038581166001600160a01b03199283161790925560048054858416908316811782556005805486861694169390931790925560006007819055620151806008556064600955600a5560025460405163095ea7b360e01b815291820192909252600019602482015291169063095ea7b3906044016020604051808303816000875af1158015613530573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135549190614a5e565b5050505050565b600d80546001600160a01b0319166001600160a01b03891690811790915560408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa1580156135b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135d89190614972565b600e80546001600160a01b0319166001600160a01b039290921691821790556040805163038fff2d60e41b81529051600092916338fff2d09160048083019260209291908290030181865afa158015613635573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136599190614827565b601081905560118890556012879055601386905560065490915061369d906001600160a01b031673ba12222222228d8ba445958a75a0704d566bf2c8600019612aa1565b600d54600e546136bc916001600160a01b039182169116600019612aa1565b83516136cf906015906020870190614352565b5082516136e390601690602086019061439d565b5081516136f7906017906020850190614352565b5060155460005b818160ff1610156139ac5760196040518060a0016040528060158460ff168154811061372c5761372c6147e2565b9060005260206000200154815260200160178460ff1681548110613752576137526147e2565b9060005260206000200154815260200160178460016137719190614dd3565b60ff1681548110613784576137846147e2565b906000526020600020015481526020016000815260200160006040516020016137b6919060ff91909116815260200190565b60408051601f1981840301815291815291528254600181810185556000948552602094859020845160059093020191825593830151938101939093558101516002830155606081015160038301556080810151909190600482019061381b9082614dec565b505050601a6040518060a0016040528060158460ff1660018761383e9190614a4b565b6138489190614a4b565b81548110613858576138586147e2565b9060005260206000200154815260200160178460ff16866138799190614a4b565b81548110613889576138896147e2565b9060005260206000200154815260200160178460ff166001876138ac9190614a4b565b6138b69190614a4b565b815481106138c6576138c66147e2565b906000526020600020015481526020016000815260200160006040516020016138f8919060ff91909116815260200190565b60408051601f1981840301815291815291528254600181810185556000948552602094859020845160059093020191825593830151938101939093558101516002830155606081015160038301556080810151909190600482019061395d9082614dec565b505060188054600181018255600091909152600160c81b7fb13d2d76d1f4b7be834882e410b3e3a8afaf69f83600ae24db354391d2378d2e9091015550806139a481614eac565b9150506136fe565b50506018805460018181018355600092909252600160c81b7fb13d2d76d1f4b7be834882e410b3e3a8afaf69f83600ae24db354391d2378d2e909101556014805460ff191690911790555050505050505050565b600d546040516370a0823160e01b81523060048201526001600160a01b039091169063a9059cbb90839083906370a0823190602401602060405180830381865afa158015613a52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a769190614827565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af1158015613ac1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109949190614a5e565b6000613b3a826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612bee9092919063ffffffff16565b805190915015612be95780806020019051810190613b589190614a5e565b612be95760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016107f1565b606082471015613c185760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016107f1565b843b613c665760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107f1565b600080866001600160a01b03168587604051613c829190614ecb565b60006040518083038185875af1925050503d8060008114613cbf576040519150601f19603f3d011682016040523d82523d6000602084013e613cc4565b606091505b5091509150613cd4828286614319565b979650505050505050565b6006546040516370a0823160e01b8152306004820152600091829182916001600160a01b0316906370a0823190602401602060405180830381865afa158015613d2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d509190614827565b90506000613d5d85612036565b600e546040516370a0823160e01b81523060048201529192506000916001600160a01b03909116906370a0823190602401602060405180830381865afa158015613dab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dcf9190614827565b90506000600d60009054906101000a90046001600160a01b03166001600160a01b03166399530b066040518163ffffffff1660e01b8152600401602060405180830381865afa158015613e26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e4a9190614827565b9050600081613e6185670de0b6b3a764000061498f565b613e6b91906149ae565b600d546040516370a0823160e01b81523060048201529192506000916001600160a01b03909116906370a0823190602401602060405180830381865afa158015613eb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613edd9190614827565b905080821115613f18579050806000670de0b6b3a7640000613eff858461498f565b613f0991906149ae565b9050613f1481610d0e565b9950505b811561405f57600d54604051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d906024016020604051808303816000875af1158015613f69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8d9190614827565b5060145460ff161561405f57613fa4600183614a4b565b600d546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015613fec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140109190614827565b61401a9083614a4b565b101561405f5760405162461bcd60e51b815260206004820152601460248201527316559055531515d2551211149055d1905253115160621b60448201526064016107f1565b600e546040516370a0823160e01b815230600482015260009186916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156140ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140d09190614827565b6140da9190614a4b565b905080156140eb576140eb81614287565b6006546040516370a0823160e01b815230600482015260009189916001600160a01b03909116906370a0823190602401602060405180830381865afa158015614138573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061415c9190614827565b6141669190614a4b565b90508a8110614177578a9950614187565b985088614184818c614a4b565b98505b5050505050505050915091565b60008183106141a357816141a5565b825b90505b92915050565b8060196000815481106141c3576141c36147e2565b6000918252602080832060036005909302019190910192909255604080516080810182523080825293810183905280820193909352606083018290525163945bcec960e01b815273ba12222222228d8ba445958a75a0704d566bf2c89163945bcec991614240919060199060169087906018904290600401614f5a565b6000604051808303816000875af115801561425f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612be991908101906150fb565b80601a60008154811061429c5761429c6147e2565b6000918252602080832060036005909302019190910192909255604080516080810182523080825293810183905280820193909352606083018290525163945bcec960e01b815273ba12222222228d8ba445958a75a0704d566bf2c89163945bcec9916142409190601a9060169087906018904290600401614f5a565b60608315614328575081610dc6565b8251156143385782518084602001fd5b8160405162461bcd60e51b81526004016107f1919061446d565b82805482825590600052602060002090810192821561438d579160200282015b8281111561438d578251825591602001919060010190614372565b506143999291506143f2565b5090565b82805482825590600052602060002090810192821561438d579160200282015b8281111561438d57825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906143bd565b5b8082111561439957600081556001016143f3565b6001600160a01b038116811461203357600080fd5b803561442781614407565b919050565b60006020828403121561443e57600080fd5b8135610dc681614407565b60005b8381101561446457818101518382015260200161444c565b50506000910152565b602081526000825180602084015261448c816040850160208701614449565b601f01601f19169190910160400192915050565b6000602082840312156144b257600080fd5b5035919050565b801515811461203357600080fd5b6000602082840312156144d957600080fd5b8135610dc6816144b9565b600080602083850312156144f757600080fd5b823567ffffffffffffffff8082111561450f57600080fd5b818501915085601f83011261452357600080fd5b81358181111561453257600080fd5b86602082850101111561454457600080fd5b60209290920196919550909350505050565b634e487b7160e01b600052604160045260246000fd5b604051610120810167ffffffffffffffff8111828210171561459057614590614556565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156145bf576145bf614556565b604052919050565b600067ffffffffffffffff8211156145e1576145e1614556565b5060051b60200190565b600082601f8301126145fc57600080fd5b8135602061461161460c836145c7565b614596565b82815260059290921b8401810191818101908684111561463057600080fd5b8286015b8481101561464b5780358352918301918301614634565b509695505050505050565b600082601f83011261466757600080fd5b8135602061467761460c836145c7565b82815260059290921b8401810191818101908684111561469657600080fd5b8286015b8481101561464b5780356146ad81614407565b835291830191830161469a565b60008060008060008060008060008060006101608c8e0312156146dc57600080fd5b6146e58c61441c565b9a506146f360208d0161441c565b995061470160408d0161441c565b985061470f60608d0161441c565b975061471d60808d0161441c565b965060a08c0135955060c08c0135945060e08c0135935067ffffffffffffffff806101008e0135111561474f57600080fd5b6147608e6101008f01358f016145eb565b9350806101208e0135111561477457600080fd5b6147858e6101208f01358f01614656565b9250806101408e0135111561479957600080fd5b506147ab8d6101408e01358e016145eb565b90509295989b509295989b9093969950565b6020808252600b908201526a08585d5d1a1bdc9a5e995960aa1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201614820576148206147f8565b5060010190565b60006020828403121561483957600080fd5b5051919050565b600181811c9082168061485457607f821691505b602082108103610fa657634e487b7160e01b600052602260045260246000fd5b60006020828403121561488657600080fd5b815167ffffffffffffffff8082111561489e57600080fd5b818401915084601f8301126148b257600080fd5b8151818111156148c4576148c4614556565b6148d7601f8201601f1916602001614596565b91508082528560208285010111156148ee57600080fd5b6148ff816020840160208601614449565b50949350505050565b635353425f60e01b815260008451614927816004850160208901614449565b8083019050605f60f81b806004830152855161494a816005850160208a01614449565b60059201918201528351614965816006840160208801614449565b0160060195945050505050565b60006020828403121561498457600080fd5b8151610dc681614407565b60008160001904831182151516156149a9576149a96147f8565b500290565b6000826149cb57634e487b7160e01b600052601260045260246000fd5b500490565b600061012082840312156149e357600080fd5b6149eb61456c565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152508091505092915050565b818103818111156141a8576141a86147f8565b600060208284031215614a7057600080fd5b8151610dc6816144b9565b601f821115612be957600081815260208120601f850160051c81016020861015614aa25750805b601f850160051c820191505b81811015614ac157828155600101614aae565b505050505050565b67ffffffffffffffff831115614ae157614ae1614556565b614af583614aef8354614840565b83614a7b565b6000601f841160018114614b295760008515614b115750838201355b600019600387901b1c1916600186901b178355613554565b600083815260209020601f19861690835b82811015614b5a5786850135825560209485019460019092019101614b3a565b5086821015614b775760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b600081518084526020808501945080840160005b83811015614be857815187529582019590820190600101614bcc565b509495945050505050565b600061016060018060a01b03808f1684526020818f1681860152818e166040860152818d166060860152818c1660808601528a60a08601528960c08601528860e086015282610100860152614c4a83860189614bb8565b858103610120870152875180825282890194509082019060005b81811015614c82578551851683529483019491830191600101614c64565b5050858103610140870152614c978188614bb8565b9450505050509c9b505050505050505050505050565b600060208284031215614cbf57600080fd5b815160ff81168114610dc657600080fd5b600181815b80851115614d0b578160001904821115614cf157614cf16147f8565b80851615614cfe57918102915b93841c9390800290614cd5565b509250929050565b600082614d22575060016141a8565b81614d2f575060006141a8565b8160018114614d455760028114614d4f57614d6b565b60019150506141a8565b60ff841115614d6057614d606147f8565b50506001821b6141a8565b5060208310610133831016604e8410600b8410161715614d8e575081810a6141a8565b614d988383614cd0565b8060001904821115614dac57614dac6147f8565b029392505050565b60006141a58383614d13565b808201808211156141a8576141a86147f8565b60ff81811683821601908111156141a8576141a86147f8565b815167ffffffffffffffff811115614e0657614e06614556565b614e1a81614e148454614840565b84614a7b565b602080601f831160018114614e4f5760008415614e375750858301515b600019600386901b1c1916600185901b178555614ac1565b600085815260208120601f198616915b82811015614e7e57888601518255948401946001909101908401614e5f565b5085821015614e9c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060ff821660ff8103614ec257614ec26147f8565b60010192915050565b60008251614edd818460208701614449565b9190910192915050565b6000815480845260208085019450836000528060002060005b83811015614be85781546001600160a01b031687529582019560019182019101614f00565b6000815480845260208085019450836000528060002060005b83811015614be857815487529582019560019182019101614f3e565b6000610120820160028910614f7f57634e487b7160e01b600052602160045260246000fd5b8883526101206020840152808854808352610140850191506101408160051b860101925060008a815260208120815b838110156150825761013f19888703018552815486526001820154602087015260028201546040870152600382015460608701526004820160a06080880152838154614ff981614840565b8060a08b01526001821660008114615018576001811461503457615065565b60ff19831660c08c015260c082151560051b8c01019350615065565b84885260208820885b8381101561505c5781548d820160c0015260019091019060200161503d565b8c0160c0019450505b509198505050602095909501945060059190910190600101614fae565b505050505082810360408401526150998188614ee7565b86516001600160a01b03908116606086810191909152602089015115156080870152604089015190911660a0860152870151151560c0850152905082810360e08401526150e68186614f25565b91505082610100830152979650505050505050565b6000602080838503121561510e57600080fd5b825167ffffffffffffffff81111561512557600080fd5b8301601f8101851361513657600080fd5b805161514461460c826145c7565b81815260059190911b8201830190838101908783111561516357600080fd5b928401925b82841015613cd45783518252928401929084019061516856fea26469706673582212204562973348774e80883b9d16ad72d2c136a329109f479e6341b87c41b892133d64736f6c634300081000330000000000000000000000001f8ad2cec4a2595ff3cda9e8a39c0b1be1a02014000000000000000000000000cf9d867d869ab6aaac1b9406ed3175aeb9fab49c0000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000021e19e0c9bab24000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000002804cdb9116a10bb78768d3252355a1b18067bf8f0000000000000000000000fb7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe00000000000000000000000000000000000000000000000000000000000000030000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000007b50775383d3d6f0215a8f290f2c9e2eebbeceb2000000000000000000000000804cdb9116a10bb78768d3252355a1b18067bf8f0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103a45760003560e01c8063776da470116101e9578063b252720b1161010f578063e3e1d552116100ad578063f017c92f1161007c578063f017c92f14610778578063fbfa77cf1461078b578063fcf2d0ad1461079e578063fe048ea3146107a657600080fd5b8063e3e1d55214610742578063ec38a8621461074a578063ed882c2b1461075d578063efbb5cb01461077057600080fd5b8063ce5494bb116100e9578063ce5494bb146106f5578063d0718afe14610708578063d55f92731461071b578063e02aa7ba1461072f57600080fd5b8063b252720b146106c1578063bd28f351146106d9578063c7b9d530146106e257600080fd5b806391397ab4116101875780639ec5a894116101565780639ec5a8941461067b578063a430735b1461068e578063ac00ff261461069b578063aced1661146106ae57600080fd5b806391397ab414610639578063934194ab1461064c578063939d1d271461065f57806395e80c501461067257600080fd5b80638cdfe166116101c35780638cdfe166146105f95780638e499bcf146106025780638e6350e2146106285780638ebff3781461063057600080fd5b8063776da470146105c5578063780022a0146105d8578063899552fb146105e657600080fd5b806335640a77116102ce57806360700ce71161026c5780636f392ce71161023b5780636f392ce7146105895780637085d5ed14610596578063748747e61461059f578063750521f5146105b257600080fd5b806360700ce71461054c578063650d18801461055f5780636718835f146105735780636c4a3c2a1461058057600080fd5b8063440368a3116102a8578063440368a31461051c5780634641257d146105245780635641ec031461052c5780635b916a401461053957600080fd5b806335640a77146104e357806339a172a8146104f65780633f308fa31461050957600080fd5b80631d12f28b1161034657806322f3e2d41161031557806322f3e2d41461048e57806325829410146104a657806328b7ccf7146104c75780632e1a7d4d146104d057600080fd5b80631d12f28b1461043e5780631f1fcd51146104475780631fe4a6861461047257806322ecf8231461048557600080fd5b80630f969b87116103825780630f969b87146103e457806311bc8245146103f757806313328f1d1461040a578063143b00411461042b57600080fd5b806301681a62146103a957806303ee438c146103be57806306fdde03146103dc575b600080fd5b6103bc6103b736600461442c565b6107b9565b005b6103c6610998565b6040516103d3919061446d565b60405180910390f35b6103c6610a26565b6103bc6103f23660046144a0565b610b57565b6103bc61040536600461442c565b610be0565b61041d6104183660046144a0565b610d0e565b6040519081526020016103d3565b6103bc6104393660046144c7565b610dcd565b61041d600a5481565b60065461045a906001600160a01b031681565b6040516001600160a01b0390911681526020016103d3565b60035461045a906001600160a01b031681565b61041d60105481565b610496610eaa565b60405190151581526020016103d3565b604080518082019091526005815264302e342e3360d81b60208201526103c6565b61041d60085481565b61041d6104de3660046144a0565b610f38565b61041d6104f13660046144a0565b610fac565b6103bc6105043660046144a0565b610fcd565b6103bc6105173660046144a0565b61104f565b6103bc61111e565b6103bc61131a565b600b546104969060ff1681565b600d5461045a906001600160a01b031681565b6103bc61055a3660046144a0565b611808565b61049661056d3660046144a0565b50600090565b6001546104969060ff1681565b61041d60125481565b601b546104969060ff1681565b61041d60135481565b6103bc6105ad36600461442c565b6118d7565b6103bc6105c03660046144e4565b611985565b600e5461045a906001600160a01b031681565b61041d61056d3660046144a0565b6103bc6105f43660046144a0565b611a1d565b61041d60095481565b600e5461061690600160a01b900460ff1681565b60405160ff90911681526020016103d3565b61041d611aec565b61041d60115481565b6103bc6106473660046144a0565b611b63565b61045a61065a3660046146ba565b611be5565b6103bc61066d3660046146ba565b611d23565b61041d60075481565b60045461045a906001600160a01b031681565b6014546104969060ff1681565b6103bc6106a93660046144c7565b611d4b565b60055461045a906001600160a01b031681565b60015461045a9061010090046001600160a01b031681565b61041d600c5481565b6103bc6106f036600461442c565b611e5d565b6103bc61070336600461442c565b611f0b565b61041d6107163660046144a0565b612036565b600e5461061690600160a81b900460ff1681565b61041d61073d3660046144a0565b6120ed565b61041d6120fd565b6103bc61075836600461442c565b61230b565b61049661076b3660046144a0565b6124a9565b61041d6126e6565b6103bc6107863660046144a0565b6127ee565b60025461045a906001600160a01b031681565b6103bc612870565b61045a6107b43660046144a0565b612a77565b6107c1612c05565b6001600160a01b0316336001600160a01b0316146107fa5760405162461bcd60e51b81526004016107f1906147bd565b60405180910390fd5b6006546001600160a01b03908116908216036108405760405162461bcd60e51b8152602060048201526005602482015264085dd85b9d60da1b60448201526064016107f1565b6002546001600160a01b03908116908216036108885760405162461bcd60e51b81526020600482015260076024820152662173686172657360c81b60448201526064016107f1565b606060005b815181101561090f578181815181106108a8576108a86147e2565b60200260200101516001600160a01b0316836001600160a01b0316036108fd5760405162461bcd60e51b815260206004820152600a602482015269085c1c9bdd1958dd195960b21b60448201526064016107f1565b806109078161480e565b91505061088d565b5061099461091b612c05565b6040516370a0823160e01b81523060048201526001600160a01b038516906370a0823190602401602060405180830381865afa15801561095f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109839190614827565b6001600160a01b0385169190612c73565b5050565b600080546109a590614840565b80601f01602080910402602001604051908101604052809291908181526020018280546109d190614840565b8015610a1e5780601f106109f357610100808354040283529160200191610a1e565b820191906000526020600020905b815481529060010190602001808311610a0157829003601f168201915b505050505081565b600654604080516395d89b4160e01b815290516060926001600160a01b0316916395d89b419160048083019260009291908290030181865afa158015610a70573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610a989190810190614874565b6040805180820190915260078152665048414e544f4d60c81b6020820152600e60009054906101000a90046001600160a01b03166001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015610b09573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b319190810190614874565b604051602001610b4393929190614908565b604051602081830303815290604052905090565b6003546001600160a01b0316331480610b885750610b73612c05565b6001600160a01b0316336001600160a01b0316145b610ba45760405162461bcd60e51b81526004016107f1906147bd565b600a8190556040518181527fa68ba126373d04c004c5748c300c9fca12bd444b3d4332e261f3bd2bac4a8600906020015b60405180910390a150565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c579190614972565b6001600160a01b0316336001600160a01b03161480610c8e5750610c79612c05565b6001600160a01b0316336001600160a01b0316145b610caa5760405162461bcd60e51b81526004016107f1906147bd565b6040516001600160a01b03821681527fc8db9c35f716b87af1fbb83f03c78646061931269301fd7ba6dcf189b4cdc2fc9060200160405180910390a1600180546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b600080670de0b6b3a7640000600e60009054906101000a90046001600160a01b03166001600160a01b031663679aefce6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d919190614827565b610d9b908561498f565b610da591906149ae565b600e54600654919250610dc69183916001600160a01b039081169116612ca3565b9392505050565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e449190614972565b6001600160a01b0316336001600160a01b03161480610e7b5750610e66612c05565b6001600160a01b0316336001600160a01b0316145b610e975760405162461bcd60e51b81526004016107f1906147bd565b6014805460ff1916911515919091179055565b6002546040516339ebf82360e01b815230600482015260009182916001600160a01b03909116906339ebf8239060240161012060405180830381865afa158015610ef8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1c91906149d0565b604001511180610f3357506000610f316126e6565b115b905090565b6002546000906001600160a01b03163314610f7e5760405162461bcd60e51b8152602060048201526006602482015265085d985d5b1d60d21b60448201526064016107f1565b6000610f8983612dca565b600654909350909150610fa6906001600160a01b03163383612c73565b50919050565b60178181548110610fbc57600080fd5b600091825260209091200154905081565b6003546001600160a01b0316331480610ffe5750610fe9612c05565b6001600160a01b0316336001600160a01b0316145b61101a5760405162461bcd60e51b81526004016107f1906147bd565b60078190556040518181527fbb2c369a0355a34b02ab5fce0643150c87e1c8dfe7c918d465591879f57948b190602001610bd5565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c69190614972565b6001600160a01b0316336001600160a01b031614806110fd57506110e8612c05565b6001600160a01b0316336001600160a01b0316145b6111195760405162461bcd60e51b81526004016107f1906147bd565b601355565b6005546001600160a01b031633148061114157506003546001600160a01b031633145b80611164575061114f612c05565b6001600160a01b0316336001600160a01b0316145b806111f65750600260009054906101000a90046001600160a01b03166001600160a01b031663452a93206040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e19190614972565b6001600160a01b0316336001600160a01b0316145b806112885750600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa15801561124f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112739190614972565b6001600160a01b0316336001600160a01b0316145b6112a45760405162461bcd60e51b81526004016107f1906147bd565b6002546040805163bf3759b560e01b81529051611318926001600160a01b03169163bf3759b59160048083019260209291908290030181865afa1580156112ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113139190614827565b612e88565b565b6005546001600160a01b031633148061133d57506003546001600160a01b031633145b80611360575061134b612c05565b6001600160a01b0316336001600160a01b0316145b806113f25750600260009054906101000a90046001600160a01b03166001600160a01b031663452a93206040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113dd9190614972565b6001600160a01b0316336001600160a01b0316145b806114845750600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa15801561144b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146f9190614972565b6001600160a01b0316336001600160a01b0316145b6114a05760405162461bcd60e51b81526004016107f1906147bd565b6000806000600260009054906101000a90046001600160a01b03166001600160a01b031663bf3759b56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151c9190614827565b600b5490915060009060ff1615611579576000611537612fa7565b9050828110156115525761154b8184614a4b565b9350611567565b82811115611567576115648382614a4b565b94505b6115718484614a4b565b91505061158a565b6115828261315d565b919550935090505b6002546040516339ebf82360e01b81523060048201526000916001600160a01b0316906339ebf8239060240161012060405180830381865afa1580156115d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f891906149d0565b60c001516002546040516328766ebf60e21b81526004810188905260248101879052604481018590529192506001600160a01b03169063a1d9bafc906064016020604051808303816000875af1158015611656573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167a9190614827565b925061168583612e88565b60015460ff1680156116a6575060015461010090046001600160a01b031615155b156117785760015460405163c70fa00b60e01b815260048101879052602481018690526044810184905260648101859052608481018390526101009091046001600160a01b03169063c70fa00b9060a401602060405180830381865afa158015611714573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117389190614a5e565b6117735760405162461bcd60e51b815260206004820152600c60248201526b216865616c7468636865636b60a01b60448201526064016107f1565b6117b9565b604051600181527ff769f6bf659bbbdabf212d830720ce893eedc57f25ebb8e44edf5b300618a35b9060200160405180910390a16001805460ff1916811790555b6040805186815260208101869052908101839052606081018490527f4c0f499ffe6befa0ca7c826b0916cf87bea98de658013e76938489368d60d5099060800160405180910390a15050505050565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa15801561185b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187f9190614972565b6001600160a01b0316336001600160a01b031614806118b657506118a1612c05565b6001600160a01b0316336001600160a01b0316145b6118d25760405162461bcd60e51b81526004016107f1906147bd565b601155565b6003546001600160a01b031633148061190857506118f3612c05565b6001600160a01b0316336001600160a01b0316145b6119245760405162461bcd60e51b81526004016107f1906147bd565b6001600160a01b03811661193757600080fd5b600580546001600160a01b0319166001600160a01b0383169081179091556040519081527f2f202ddb4a2e345f6323ed90f8fc8559d770a7abbbeee84dde8aca3351fe715490602001610bd5565b6003546001600160a01b03163314806119b657506119a1612c05565b6001600160a01b0316336001600160a01b0316145b6119d25760405162461bcd60e51b81526004016107f1906147bd565b60006119df828483614ac9565b507f300e67d5a415b6d015a471d9c7b95dd58f3e8290af965e84e0f845de2996dda68282604051611a11929190614b89565b60405180910390a15050565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a949190614972565b6001600160a01b0316336001600160a01b03161480611acb5750611ab6612c05565b6001600160a01b0316336001600160a01b0316145b611ae75760405162461bcd60e51b81526004016107f1906147bd565b601255565b6002546040516339ebf82360e01b81523060048201526000916001600160a01b0316906339ebf8239060240161012060405180830381865afa158015611b36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5a91906149d0565b60c00151905090565b6003546001600160a01b0316331480611b945750611b7f612c05565b6001600160a01b0316336001600160a01b0316145b611bb05760405162461bcd60e51b81526004016107f1906147bd565b60098190556040518181527fd94596337df4c2f0f44d30a7fc5db1c7bb60d9aca4185ed77c6fd96eb45ec29890602001610bd5565b601b5460009060ff16611c235760405162461bcd60e51b815260206004820152600660248201526521636c6f6e6560d01b60448201526064016107f1565b604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81523060601b601482018190526e5af43d82803e903d91602b57fd5bf360881b6028830152906037816000f0925050816001600160a01b031663939d1d278e8e8e8e8e8e8e8e8e8e8e6040518c63ffffffff1660e01b8152600401611cad9b9a99989796959493929190614bf3565b600060405180830381600087803b158015611cc757600080fd5b505af1158015611cdb573d6000803e3d6000fd5b50506040516001600160a01b03851692507f783540fb4221a3238720dc7038937d0d79982bcf895274aa6ad179f82cf0d53c9150600090a2509b9a5050505050505050505050565b611d2f8b8b8b8b613391565b611d3e8787878787878761355b565b5050505050505050505050565b600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dc29190614972565b6001600160a01b0316336001600160a01b03161480611df95750611de4612c05565b6001600160a01b0316336001600160a01b0316145b611e155760405162461bcd60e51b81526004016107f1906147bd565b60405181151581527ff769f6bf659bbbdabf212d830720ce893eedc57f25ebb8e44edf5b300618a35b9060200160405180910390a16001805460ff1916911515919091179055565b6003546001600160a01b0316331480611e8e5750611e79612c05565b6001600160a01b0316336001600160a01b0316145b611eaa5760405162461bcd60e51b81526004016107f1906147bd565b6001600160a01b038116611ebd57600080fd5b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527f352ececae6d7d1e6d26bcf2c549dfd55be1637e9b22dc0cf3b71ddb36097a6b490602001610bd5565b6002546001600160a01b03163314611f2257600080fd5b6002546040805163fbfa77cf60e01b815290516001600160a01b039283169284169163fbfa77cf9160048083019260209291908290030181865afa158015611f6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f929190614972565b6001600160a01b031614611fa557600080fd5b611fae81613a00565b6006546040516370a0823160e01b81523060048201526120339183916001600160a01b03909116906370a0823190602401602060405180830381865afa158015611ffc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120209190614827565b6006546001600160a01b03169190612c73565b50565b600080600e60009054906101000a90046001600160a01b03166001600160a01b031663679aefce6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561208c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120b09190614827565b6120c284670de0b6b3a764000061498f565b6120cc91906149ae565b600654600e54919250610dc69183916001600160a01b039081169116612ca3565b60158181548110610fbc57600080fd5b600d546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a0823190602401602060405180830381865afa15801561214a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216e9190614827565b9050600d60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121e79190614827565b6000036121f657600091505090565b600d5460408051634ca9858360e11b815290516000926001600160a01b0316916399530b069160048083019260209291908290030181865afa158015612240573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122649190614827565b90506000600e60009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122df9190614cad565b60ff1690506122ef81600a614db4565b6122f9838561498f565b61230391906149ae565b935050505090565b6003546001600160a01b031633146123535760405162461bcd60e51b815260206004820152600b60248201526a085cdd1c985d1959da5cdd60aa1b60448201526064016107f1565b6001600160a01b03811661236657600080fd5b6002546004805460405163095ea7b360e01b81526001600160a01b0391821692810192909252600060248301529091169063095ea7b3906044016020604051808303816000875af11580156123bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e39190614a5e565b50600480546001600160a01b0319166001600160a01b03838116918217835560025460405163095ea7b360e01b8152938401929092526000196024840152169063095ea7b3906044016020604051808303816000875af115801561244b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061246f9190614a5e565b506040516001600160a01b03821681527fafbb66abf8f3b719799940473a4052a3717cdd8e40fb6c8a3faadab316b1a06990602001610bd5565b600080806002546040516339ebf82360e01b81523060048201529192506000916001600160a01b03909116906339ebf8239060240161012060405180830381865afa1580156124fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252091906149d0565b90508060200151600003612538575060009392505050565b60075460a082015161254a9042614a4b565b101561255a575060009392505050565b60085460a082015161256c9042614a4b565b1061257b575060019392505050565b6002546040805163bf3759b560e01b815290516000926001600160a01b03169163bf3759b59160048083019260209291908290030181865afa1580156125c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e99190614827565b9050600a5481111561260057506001949350505050565b600061260a6126e6565b90508260c00151600a548261261f9190614dc0565b10156126315750600195945050505050565b60008360c001518211156126515760c084015161264e9083614a4b565b90505b6002546040805163112c1f9b60e01b815290516000926001600160a01b03169163112c1f9b9160048083019260209291908290030181865afa15801561269b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126bf9190614827565b90506126cb8282614dc0565b866009546126d9919061498f565b1098975050505050505050565b6000806126f16120fd565b600e546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015612739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275d9190614827565b6127679190614dc0565b905061277281610d0e565b6006546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156127ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127de9190614827565b6127e89190614dc0565b91505090565b6003546001600160a01b031633148061281f575061280a612c05565b6001600160a01b0316336001600160a01b0316145b61283b5760405162461bcd60e51b81526004016107f1906147bd565b60088190556040518181527f5430e11864ad7aa9775b07d12657fe52df9aa2ba734355bd8ef8747be2c800c590602001610bd5565b6003546001600160a01b03163314806128a1575061288c612c05565b6001600160a01b0316336001600160a01b0316145b806129335750600260009054906101000a90046001600160a01b03166001600160a01b031663452a93206040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291e9190614972565b6001600160a01b0316336001600160a01b0316145b806129c55750600260009054906101000a90046001600160a01b03166001600160a01b03166388a8d6026040518163ffffffff1660e01b8152600401602060405180830381865afa15801561298c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129b09190614972565b6001600160a01b0316336001600160a01b0316145b6129e15760405162461bcd60e51b81526004016107f1906147bd565b600b805460ff191660011790556002546040805163507257cd60e11b815290516001600160a01b039092169163a0e4af9a9160048082019260009290919082900301818387803b158015612a3457600080fd5b505af1158015612a48573d6000803e3d6000fd5b50506040517f97e963041e952738788b9d4871d854d282065b8f90a464928d6528f2e9a4fd0b925060009150a1565b60168181548110612a8757600080fd5b6000918252602090912001546001600160a01b0316905081565b801580612b1b5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612af5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b199190614827565b155b612b865760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016107f1565b6040516001600160a01b038316602482015260448101829052612be990849063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613ae5565b505050565b6060612bfd8484600085613bb7565b949350505050565b60025460408051635aa6e67560e01b815290516000926001600160a01b031691635aa6e6759160048083019260209291908290030181865afa158015612c4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f339190614972565b6040516001600160a01b038316602482015260448101829052612be990849063a9059cbb60e01b90606401612bb2565b600080836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ce4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d089190614cad565b60ff1690506000836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d719190614cad565b60ff169050818111612da157612d878183614a4b565b612d9290600a614db4565b612d9c90876149ae565b612dc0565b612dab8282614a4b565b612db690600a614db4565b612dc0908761498f565b9695505050505050565b6006546040516370a0823160e01b8152306004820152600091829182916001600160a01b0316906370a0823190602401602060405180830381865afa158015612e17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e3b9190614827565b905083811015612e61576000612e518286614a4b565b9050612e5c81613cdf565b509350505b612e7484612e6f8386614dc0565b614194565b9250612e808385614a4b565b915050915091565b6006546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015612ed1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef59190614827565b905081811115610994576000612f0b8383614a4b565b9050612f1981601354614194565b9050612f24816141ae565b600d60009054906101000a90046001600160a01b03166001600160a01b031663d0e30db06040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612f79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f9d9190614827565b505042600c555050565b600d546040516370a0823160e01b81523060048201526000916001600160a01b031690632e1a7d4d9082906370a0823190602401602060405180830381865afa158015612ff8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061301c9190614827565b6040518263ffffffff1660e01b815260040161303a91815260200190565b6020604051808303816000875af1158015613059573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307d9190614827565b50600e546040516370a0823160e01b81523060048201526130f1916001600160a01b0316906370a0823190602401602060405180830381865afa1580156130c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130ec9190614827565b614287565b6006546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015613139573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f339190614827565b6002546040516339ebf82360e01b81523060048201526000918291849183916001600160a01b03909116906339ebf8239060240161012060405180830381865afa1580156131af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131d391906149d0565b60c00151905060006131e36126e6565b9050818111156131fe576131f78282614a4b565b945061320b565b6132088183614a4b565b93505b60006132178685614dc0565b6006546040516370a0823160e01b81523060048201529192506000916001600160a01b03909116906370a0823190602401602060405180830381865afa158015613265573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132899190614827565b90508082111561338657600061329f8284614a4b565b905060006132ac82613cdf565b915050888110156132c8576132c1818a614a4b565b98506132e3565b6132d28982614a4b565b6132dc9089614dc0565b9750600098505b6006546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa15801561332b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061334f9190614827565b9250888310156133655782985060009650613383565b61336f8988614dc0565b831015613383576133808984614a4b565b96505b50505b505050509193909250565b6006546001600160a01b0316156133ea5760405162461bcd60e51b815260206004820152601c60248201527f537472617465677920616c726561647920696e697469616c697a65640000000060448201526064016107f1565b600280546001600160a01b0319166001600160a01b03861690811790915560408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa158015613443573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134679190614972565b600680546001600160a01b0319166001600160a01b039290921691821790556134939085600019612aa1565b600380546001600160a01b038581166001600160a01b03199283161790925560048054858416908316811782556005805486861694169390931790925560006007819055620151806008556064600955600a5560025460405163095ea7b360e01b815291820192909252600019602482015291169063095ea7b3906044016020604051808303816000875af1158015613530573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135549190614a5e565b5050505050565b600d80546001600160a01b0319166001600160a01b03891690811790915560408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa1580156135b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135d89190614972565b600e80546001600160a01b0319166001600160a01b039290921691821790556040805163038fff2d60e41b81529051600092916338fff2d09160048083019260209291908290030181865afa158015613635573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136599190614827565b601081905560118890556012879055601386905560065490915061369d906001600160a01b031673ba12222222228d8ba445958a75a0704d566bf2c8600019612aa1565b600d54600e546136bc916001600160a01b039182169116600019612aa1565b83516136cf906015906020870190614352565b5082516136e390601690602086019061439d565b5081516136f7906017906020850190614352565b5060155460005b818160ff1610156139ac5760196040518060a0016040528060158460ff168154811061372c5761372c6147e2565b9060005260206000200154815260200160178460ff1681548110613752576137526147e2565b9060005260206000200154815260200160178460016137719190614dd3565b60ff1681548110613784576137846147e2565b906000526020600020015481526020016000815260200160006040516020016137b6919060ff91909116815260200190565b60408051601f1981840301815291815291528254600181810185556000948552602094859020845160059093020191825593830151938101939093558101516002830155606081015160038301556080810151909190600482019061381b9082614dec565b505050601a6040518060a0016040528060158460ff1660018761383e9190614a4b565b6138489190614a4b565b81548110613858576138586147e2565b9060005260206000200154815260200160178460ff16866138799190614a4b565b81548110613889576138896147e2565b9060005260206000200154815260200160178460ff166001876138ac9190614a4b565b6138b69190614a4b565b815481106138c6576138c66147e2565b906000526020600020015481526020016000815260200160006040516020016138f8919060ff91909116815260200190565b60408051601f1981840301815291815291528254600181810185556000948552602094859020845160059093020191825593830151938101939093558101516002830155606081015160038301556080810151909190600482019061395d9082614dec565b505060188054600181018255600091909152600160c81b7fb13d2d76d1f4b7be834882e410b3e3a8afaf69f83600ae24db354391d2378d2e9091015550806139a481614eac565b9150506136fe565b50506018805460018181018355600092909252600160c81b7fb13d2d76d1f4b7be834882e410b3e3a8afaf69f83600ae24db354391d2378d2e909101556014805460ff191690911790555050505050505050565b600d546040516370a0823160e01b81523060048201526001600160a01b039091169063a9059cbb90839083906370a0823190602401602060405180830381865afa158015613a52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a769190614827565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af1158015613ac1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109949190614a5e565b6000613b3a826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612bee9092919063ffffffff16565b805190915015612be95780806020019051810190613b589190614a5e565b612be95760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016107f1565b606082471015613c185760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016107f1565b843b613c665760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107f1565b600080866001600160a01b03168587604051613c829190614ecb565b60006040518083038185875af1925050503d8060008114613cbf576040519150601f19603f3d011682016040523d82523d6000602084013e613cc4565b606091505b5091509150613cd4828286614319565b979650505050505050565b6006546040516370a0823160e01b8152306004820152600091829182916001600160a01b0316906370a0823190602401602060405180830381865afa158015613d2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d509190614827565b90506000613d5d85612036565b600e546040516370a0823160e01b81523060048201529192506000916001600160a01b03909116906370a0823190602401602060405180830381865afa158015613dab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dcf9190614827565b90506000600d60009054906101000a90046001600160a01b03166001600160a01b03166399530b066040518163ffffffff1660e01b8152600401602060405180830381865afa158015613e26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e4a9190614827565b9050600081613e6185670de0b6b3a764000061498f565b613e6b91906149ae565b600d546040516370a0823160e01b81523060048201529192506000916001600160a01b03909116906370a0823190602401602060405180830381865afa158015613eb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613edd9190614827565b905080821115613f18579050806000670de0b6b3a7640000613eff858461498f565b613f0991906149ae565b9050613f1481610d0e565b9950505b811561405f57600d54604051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d906024016020604051808303816000875af1158015613f69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8d9190614827565b5060145460ff161561405f57613fa4600183614a4b565b600d546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015613fec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140109190614827565b61401a9083614a4b565b101561405f5760405162461bcd60e51b815260206004820152601460248201527316559055531515d2551211149055d1905253115160621b60448201526064016107f1565b600e546040516370a0823160e01b815230600482015260009186916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156140ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140d09190614827565b6140da9190614a4b565b905080156140eb576140eb81614287565b6006546040516370a0823160e01b815230600482015260009189916001600160a01b03909116906370a0823190602401602060405180830381865afa158015614138573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061415c9190614827565b6141669190614a4b565b90508a8110614177578a9950614187565b985088614184818c614a4b565b98505b5050505050505050915091565b60008183106141a357816141a5565b825b90505b92915050565b8060196000815481106141c3576141c36147e2565b6000918252602080832060036005909302019190910192909255604080516080810182523080825293810183905280820193909352606083018290525163945bcec960e01b815273ba12222222228d8ba445958a75a0704d566bf2c89163945bcec991614240919060199060169087906018904290600401614f5a565b6000604051808303816000875af115801561425f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612be991908101906150fb565b80601a60008154811061429c5761429c6147e2565b6000918252602080832060036005909302019190910192909255604080516080810182523080825293810183905280820193909352606083018290525163945bcec960e01b815273ba12222222228d8ba445958a75a0704d566bf2c89163945bcec9916142409190601a9060169087906018904290600401614f5a565b60608315614328575081610dc6565b8251156143385782518084602001fd5b8160405162461bcd60e51b81526004016107f1919061446d565b82805482825590600052602060002090810192821561438d579160200282015b8281111561438d578251825591602001919060010190614372565b506143999291506143f2565b5090565b82805482825590600052602060002090810192821561438d579160200282015b8281111561438d57825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906143bd565b5b8082111561439957600081556001016143f3565b6001600160a01b038116811461203357600080fd5b803561442781614407565b919050565b60006020828403121561443e57600080fd5b8135610dc681614407565b60005b8381101561446457818101518382015260200161444c565b50506000910152565b602081526000825180602084015261448c816040850160208701614449565b601f01601f19169190910160400192915050565b6000602082840312156144b257600080fd5b5035919050565b801515811461203357600080fd5b6000602082840312156144d957600080fd5b8135610dc6816144b9565b600080602083850312156144f757600080fd5b823567ffffffffffffffff8082111561450f57600080fd5b818501915085601f83011261452357600080fd5b81358181111561453257600080fd5b86602082850101111561454457600080fd5b60209290920196919550909350505050565b634e487b7160e01b600052604160045260246000fd5b604051610120810167ffffffffffffffff8111828210171561459057614590614556565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156145bf576145bf614556565b604052919050565b600067ffffffffffffffff8211156145e1576145e1614556565b5060051b60200190565b600082601f8301126145fc57600080fd5b8135602061461161460c836145c7565b614596565b82815260059290921b8401810191818101908684111561463057600080fd5b8286015b8481101561464b5780358352918301918301614634565b509695505050505050565b600082601f83011261466757600080fd5b8135602061467761460c836145c7565b82815260059290921b8401810191818101908684111561469657600080fd5b8286015b8481101561464b5780356146ad81614407565b835291830191830161469a565b60008060008060008060008060008060006101608c8e0312156146dc57600080fd5b6146e58c61441c565b9a506146f360208d0161441c565b995061470160408d0161441c565b985061470f60608d0161441c565b975061471d60808d0161441c565b965060a08c0135955060c08c0135945060e08c0135935067ffffffffffffffff806101008e0135111561474f57600080fd5b6147608e6101008f01358f016145eb565b9350806101208e0135111561477457600080fd5b6147858e6101208f01358f01614656565b9250806101408e0135111561479957600080fd5b506147ab8d6101408e01358e016145eb565b90509295989b509295989b9093969950565b6020808252600b908201526a08585d5d1a1bdc9a5e995960aa1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201614820576148206147f8565b5060010190565b60006020828403121561483957600080fd5b5051919050565b600181811c9082168061485457607f821691505b602082108103610fa657634e487b7160e01b600052602260045260246000fd5b60006020828403121561488657600080fd5b815167ffffffffffffffff8082111561489e57600080fd5b818401915084601f8301126148b257600080fd5b8151818111156148c4576148c4614556565b6148d7601f8201601f1916602001614596565b91508082528560208285010111156148ee57600080fd5b6148ff816020840160208601614449565b50949350505050565b635353425f60e01b815260008451614927816004850160208901614449565b8083019050605f60f81b806004830152855161494a816005850160208a01614449565b60059201918201528351614965816006840160208801614449565b0160060195945050505050565b60006020828403121561498457600080fd5b8151610dc681614407565b60008160001904831182151516156149a9576149a96147f8565b500290565b6000826149cb57634e487b7160e01b600052601260045260246000fd5b500490565b600061012082840312156149e357600080fd5b6149eb61456c565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152508091505092915050565b818103818111156141a8576141a86147f8565b600060208284031215614a7057600080fd5b8151610dc6816144b9565b601f821115612be957600081815260208120601f850160051c81016020861015614aa25750805b601f850160051c820191505b81811015614ac157828155600101614aae565b505050505050565b67ffffffffffffffff831115614ae157614ae1614556565b614af583614aef8354614840565b83614a7b565b6000601f841160018114614b295760008515614b115750838201355b600019600387901b1c1916600186901b178355613554565b600083815260209020601f19861690835b82811015614b5a5786850135825560209485019460019092019101614b3a565b5086821015614b775760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b600081518084526020808501945080840160005b83811015614be857815187529582019590820190600101614bcc565b509495945050505050565b600061016060018060a01b03808f1684526020818f1681860152818e166040860152818d166060860152818c1660808601528a60a08601528960c08601528860e086015282610100860152614c4a83860189614bb8565b858103610120870152875180825282890194509082019060005b81811015614c82578551851683529483019491830191600101614c64565b5050858103610140870152614c978188614bb8565b9450505050509c9b505050505050505050505050565b600060208284031215614cbf57600080fd5b815160ff81168114610dc657600080fd5b600181815b80851115614d0b578160001904821115614cf157614cf16147f8565b80851615614cfe57918102915b93841c9390800290614cd5565b509250929050565b600082614d22575060016141a8565b81614d2f575060006141a8565b8160018114614d455760028114614d4f57614d6b565b60019150506141a8565b60ff841115614d6057614d606147f8565b50506001821b6141a8565b5060208310610133831016604e8410600b8410161715614d8e575081810a6141a8565b614d988383614cd0565b8060001904821115614dac57614dac6147f8565b029392505050565b60006141a58383614d13565b808201808211156141a8576141a86147f8565b60ff81811683821601908111156141a8576141a86147f8565b815167ffffffffffffffff811115614e0657614e06614556565b614e1a81614e148454614840565b84614a7b565b602080601f831160018114614e4f5760008415614e375750858301515b600019600386901b1c1916600185901b178555614ac1565b600085815260208120601f198616915b82811015614e7e57888601518255948401946001909101908401614e5f565b5085821015614e9c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060ff821660ff8103614ec257614ec26147f8565b60010192915050565b60008251614edd818460208701614449565b9190910192915050565b6000815480845260208085019450836000528060002060005b83811015614be85781546001600160a01b031687529582019560019182019101614f00565b6000815480845260208085019450836000528060002060005b83811015614be857815487529582019560019182019101614f3e565b6000610120820160028910614f7f57634e487b7160e01b600052602160045260246000fd5b8883526101206020840152808854808352610140850191506101408160051b860101925060008a815260208120815b838110156150825761013f19888703018552815486526001820154602087015260028201546040870152600382015460608701526004820160a06080880152838154614ff981614840565b8060a08b01526001821660008114615018576001811461503457615065565b60ff19831660c08c015260c082151560051b8c01019350615065565b84885260208820885b8381101561505c5781548d820160c0015260019091019060200161503d565b8c0160c0019450505b509198505050602095909501945060059190910190600101614fae565b505050505082810360408401526150998188614ee7565b86516001600160a01b03908116606086810191909152602089015115156080870152604089015190911660a0860152870151151560c0850152905082810360e08401526150e68186614f25565b91505082610100830152979650505050505050565b6000602080838503121561510e57600080fd5b825167ffffffffffffffff81111561512557600080fd5b8301601f8101851361513657600080fd5b805161514461460c826145c7565b81815260059190911b8201830190838101908783111561516357600080fd5b928401925b82841015613cd45783518252928401929084019061516856fea26469706673582212204562973348774e80883b9d16ad72d2c136a329109f479e6341b87c41b892133d64736f6c63430008100033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001f8ad2cec4a2595ff3cda9e8a39c0b1be1a02014000000000000000000000000cf9d867d869ab6aaac1b9406ed3175aeb9fab49c0000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000021e19e0c9bab24000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000002804cdb9116a10bb78768d3252355a1b18067bf8f0000000000000000000000fb7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe00000000000000000000000000000000000000000000000000000000000000030000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000007b50775383d3d6f0215a8f290f2c9e2eebbeceb2000000000000000000000000804cdb9116a10bb78768d3252355a1b18067bf8f0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001
-----Decoded View---------------
Arg [0] : _vault (address): 0x1F8ad2cec4a2595Ff3cdA9e8a39C0b1BE1A02014
Arg [1] : _bptVault (address): 0xCf9D867d869ab6aAAC1b9406ED3175aEb9FAb49C
Arg [2] : _maxSlippageIn (uint256): 50
Arg [3] : _maxSlippageOut (uint256): 50
Arg [4] : _maxSingleInvest (uint256): 10000000000000000000000
Arg [5] : _swapPathPoolIDs (bytes32[]): System.Byte[],System.Byte[]
Arg [6] : _swapPathAssets (address[]): 0x6B175474E89094C44Da98b954EedeAC495271d0F,0x7B50775383d3D6f0215A8F290f2C9e2eEBBEceb2,0x804CdB9116a10bB78768D3252355a1b18067bF8f
Arg [7] : _swapPathAssetIndexes (uint256[]): 0,2,1
-----Encoded View---------------
19 Constructor Arguments found :
Arg [0] : 0000000000000000000000001f8ad2cec4a2595ff3cda9e8a39c0b1be1a02014
Arg [1] : 000000000000000000000000cf9d867d869ab6aaac1b9406ed3175aeb9fab49c
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000032
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000032
Arg [4] : 00000000000000000000000000000000000000000000021e19e0c9bab2400000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [7] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [9] : 804cdb9116a10bb78768d3252355a1b18067bf8f0000000000000000000000fb
Arg [10] : 7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [12] : 0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f
Arg [13] : 0000000000000000000000007b50775383d3d6f0215a8f290f2c9e2eebbeceb2
Arg [14] : 000000000000000000000000804cdb9116a10bb78768d3252355a1b18067bf8f
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [18] : 0000000000000000000000000000000000000000000000000000000000000001
Deployed Bytecode Sourcemap
124118:8617:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;63883:444;;;;;;:::i;:::-;;:::i;:::-;;34921:25;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;115811:401;;;:::i;45470:175::-;;;;;;:::i;:::-;;:::i;40940:162::-;;;;;;:::i;:::-;;:::i;109134:377::-;;;;;;:::i;:::-;;:::i;:::-;;;1528:25:1;;;1516:2;1501:18;109134:377:0;1382:177:1;117347:167:0;;;;;;:::i;:::-;;:::i;38273:28::-;;;;;;36948:18;;;;;-1:-1:-1;;;;;36948:18:0;;;;;;-1:-1:-1;;;;;2110:32:1;;;2092:51;;2080:2;2065:18;36948::0;1933:216:1;36857:25:0;;;;;-1:-1:-1;;;;;36857:25:0;;;107477:29;;;;;;48872:148;;;:::i;:::-;;;2709:14:1;;2702:22;2684:41;;2672:2;2657:18;48872:148:0;2544:187:1;35320:91:0;35389:14;;;;;;;;;;;;-1:-1:-1;;;35389:14:0;;;;35320:91;;37909:29;;;;;;60287:515;;;;;;:::i;:::-;;:::i;124485:37::-;;;;;;:::i;:::-;;:::i;43519:154::-;;;;;;:::i;:::-;;:::i;117181:158::-;;;;;;:::i;:::-;;:::i;53969:170::-;;;:::i;58430:1613::-;;;:::i;38352:25::-;;;;;;;;;107296:22;;;;;-1:-1:-1;;;;;107296:22:0;;;116861:150;;;;;;:::i;:::-;;:::i;53208:432::-;;;;;;:::i;:::-;-1:-1:-1;53281:4:0;;53208:432;34977:25;;;;;;;;;107558:29;;;;;;125467;;;;;;;;;107602:30;;;;;;42270:174;;;;;;:::i;:::-;;:::i;45948:171::-;;;;;;:::i;:::-;;:::i;107325:33::-;;;;;-1:-1:-1;;;;;107325:33:0;;;116619:195;;;;;;:::i;117019:154::-;;;;;;:::i;:::-;;:::i;38095:27::-;;;;;;107365:22;;;;;-1:-1:-1;;;107365:22:0;;;;;;;;;3958:4:1;3946:17;;;3928:36;;3916:2;3901:18;107365:22:0;3786:184:1;108161:133:0;;;:::i;107515:28::-;;;;;;44744:169;;;;;;:::i;:::-;;:::i;129675:1484::-;;;;;;:::i;:::-;;:::i;128931:736::-;;;;;;:::i;:::-;;:::i;37758:29::-;;;;;;36889:22;;;;;-1:-1:-1;;;;;36889:22:0;;;107639:30;;;;;;;;;41110:171;;;;;;:::i;:::-;;:::i;36918:21::-;;;;;-1:-1:-1;;;;;36918:21:0;;;35009:26;;;;;;;;-1:-1:-1;;;;;35009:26:0;;;107257:30;;;;;;41514:202;;;;;;:::i;:::-;;:::i;61574:281::-;;;;;;:::i;:::-;;:::i;109519:377::-;;;;;;:::i;:::-;;:::i;107394:23::-;;;;;-1:-1:-1;;;107394:23:0;;;;;;124307:32;;;;;;:::i;:::-;;:::i;108577:549::-;;;:::i;42733:269::-;;;;;;:::i;:::-;;:::i;55906:1718::-;;;;;;:::i;:::-;;:::i;108302:267::-;;;:::i;44190:154::-;;;;;;:::i;:::-;;:::i;36829:21::-;;;;;-1:-1:-1;;;;;36829:21:0;;;62284:173;;;:::i;124412:30::-;;;;;;:::i;:::-;;:::i;63883:444::-;38978:12;:10;:12::i;:::-;-1:-1:-1;;;;;38964:26:0;:10;-1:-1:-1;;;;;38964:26:0;;38956:50;;;;-1:-1:-1;;;38956:50:0;;;;;;;:::i;:::-;;;;;;;;;63975:4:::1;::::0;-1:-1:-1;;;;;63975:4:0;;::::1;63957:23:::0;;::::1;::::0;63949:41:::1;;;::::0;-1:-1:-1;;;63949:41:0;;8654:2:1;63949:41:0::1;::::0;::::1;8636:21:1::0;8693:1;8673:18;;;8666:29;-1:-1:-1;;;8711:18:1;;;8704:35;8756:18;;63949:41:0::1;8452:328:1::0;63949:41:0::1;64027:5;::::0;-1:-1:-1;;;;;64027:5:0;;::::1;64009:24:::0;;::::1;::::0;64001:44:::1;;;::::0;-1:-1:-1;;;64001:44:0;;8987:2:1;64001:44:0::1;::::0;::::1;8969:21:1::0;9026:1;9006:18;;;8999:29;-1:-1:-1;;;9044:18:1;;;9037:37;9091:18;;64001:44:0::1;8785:330:1::0;64001:44:0::1;116528:16:::0;64058:33:::1;64122:102;64142:16;:23;64138:1;:27;64122:102;;;64190:16;64207:1;64190:19;;;;;;;;:::i;:::-;;;;;;;-1:-1:-1::0;;;;;64180:29:0::1;:6;-1:-1:-1::0;;;;;64180:29:0::1;::::0;64172:52:::1;;;::::0;-1:-1:-1;;;64172:52:0;;9454:2:1;64172:52:0::1;::::0;::::1;9436:21:1::0;9493:2;9473:18;;;9466:30;-1:-1:-1;;;9512:18:1;;;9505:40;9562:18;;64172:52:0::1;9252:334:1::0;64172:52:0::1;64167:3:::0;::::1;::::0;::::1;:::i;:::-;;;;64122:102;;;;64237:82;64265:12;:10;:12::i;:::-;64279:39;::::0;-1:-1:-1;;;64279:39:0;;64312:4:::1;64279:39;::::0;::::1;2092:51:1::0;-1:-1:-1;;;;;64279:24:0;::::1;::::0;::::1;::::0;2065:18:1;;64279:39:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;64237:27:0;::::1;::::0;:82;:27:::1;:82::i;:::-;63938:389;63883:444:::0;:::o;34921:25::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;115811:401::-;116021:4;;115998:38;;;-1:-1:-1;;;115998:38:0;;;;115859:13;;-1:-1:-1;;;;;116021:4:0;;115998:36;;:38;;;;;116021:4;;115998:38;;;;;;;116021:4;115998:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;115998:38:0;;;;;;;;;;;;:::i;:::-;131250:16;;;;;;;;;;;;-1:-1:-1;;;131250:16:0;;;;116149:12;;;;;;;;;-1:-1:-1;;;;;116149:12:0;-1:-1:-1;;;;;116149:19:0;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;116149:21:0;;;;;;;;;;;;:::i;:::-;115930:259;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;115885:319;;115811:401;:::o;45470:175::-;38463:10;;-1:-1:-1;;;;;38463:10:0;38449;:24;;:54;;;38491:12;:10;:12::i;:::-;-1:-1:-1;;;;;38477:26:0;:10;-1:-1:-1;;;;;38477:26:0;;38449:54;38441:78;;;;-1:-1:-1;;;38441:78:0;;;;;;;:::i;:::-;45555:13:::1;:30:::0;;;45601:36:::1;::::0;1528:25:1;;;45601:36:0::1;::::0;1516:2:1;1501:18;45601:36:0::1;;;;;;;;45470:175:::0;:::o;40940:162::-;39435:5;;;;;;;;;-1:-1:-1;;;;;39435:5:0;-1:-1:-1;;;;;39435:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;39421:32:0;:10;-1:-1:-1;;;;;39421:32:0;;:62;;;;39471:12;:10;:12::i;:::-;-1:-1:-1;;;;;39457:26:0;:10;-1:-1:-1;;;;;39457:26:0;;39421:62;39413:86;;;;-1:-1:-1;;;39413:86:0;;;;;;;:::i;:::-;41029:28:::1;::::0;-1:-1:-1;;;;;2110:32:1;;2092:51;;41029:28:0::1;::::0;2080:2:1;2065:18;41029:28:0::1;;;;;;;41068:11;:26:::0;;-1:-1:-1;;;;;41068:26:0;;::::1;;;-1:-1:-1::0;;;;;;41068:26:0;;::::1;::::0;;;::::1;::::0;;40940:162::o;109134:377::-;109194:7;109214:27;109297:4;109258:12;;;;;;;;;-1:-1:-1;;;;;109258:12:0;-1:-1:-1;;;;;109258:20:0;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;109245:35;;:10;:35;:::i;:::-;109244:57;;;;:::i;:::-;109426:12;;109482:4;;109214:87;;-1:-1:-1;109332:171:0;;109214:87;;-1:-1:-1;;;;;109426:12:0;;;;109482:4;109332:14;:171::i;:::-;109312:191;109134:377;-1:-1:-1;;;109134:377:0:o;117347:167::-;39435:5;;;;;;;;;-1:-1:-1;;;;;39435:5:0;-1:-1:-1;;;;;39435:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;39421:32:0;:10;-1:-1:-1;;;;;39421:32:0;;:62;;;;39471:12;:10;:12::i;:::-;-1:-1:-1;;;;;39457:26:0;:10;-1:-1:-1;;;;;39457:26:0;;39421:62;39413:86;;;;-1:-1:-1;;;39413:86:0;;;;;;;:::i;:::-;117466:18:::1;:40:::0;;-1:-1:-1;;117466:40:0::1;::::0;::::1;;::::0;;;::::1;::::0;;117347:167::o;48872:148::-;48937:5;;:31;;-1:-1:-1;;;48937:31:0;;48962:4;48937:31;;;2092:51:1;48913:4:0;;;;-1:-1:-1;;;;;48937:5:0;;;;:16;;2065:18:1;;48937:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:41;;;:45;:75;;;;49011:1;48986:22;:20;:22::i;:::-;:26;48937:75;48930:82;;48872:148;:::o;60287:515::-;60402:5;;60346:13;;-1:-1:-1;;;;;60402:5:0;60380:10;:28;60372:47;;;;-1:-1:-1;;;60372:47:0;;13940:2:1;60372:47:0;;;13922:21:1;13979:1;13959:18;;;13952:29;-1:-1:-1;;;13997:18:1;;;13990:36;14043:18;;60372:47:0;13738:329:1;60372:47:0;60505:19;60558:32;60576:13;60558:17;:32::i;:::-;60682:4;;60535:55;;-1:-1:-1;60535:55:0;;-1:-1:-1;60682:42:0;;-1:-1:-1;;;;;60682:4:0;60700:10;60535:55;60682:17;:42::i;:::-;60361:441;60287:515;;;:::o;124485:37::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;124485:37:0;:::o;43519:154::-;38463:10;;-1:-1:-1;;;;;38463:10:0;38449;:24;;:54;;;38491:12;:10;:12::i;:::-;-1:-1:-1;;;;;38477:26:0;:10;-1:-1:-1;;;;;38477:26:0;;38449:54;38441:78;;;;-1:-1:-1;;;38441:78:0;;;;;;;:::i;:::-;43597:14:::1;:23:::0;;;43636:29:::1;::::0;1528:25:1;;;43636:29:0::1;::::0;1516:2:1;1501:18;43636:29:0::1;1382:177:1::0;117181:158:0;39435:5;;;;;;;;;-1:-1:-1;;;;;39435:5:0;-1:-1:-1;;;;;39435:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;39421:32:0;:10;-1:-1:-1;;;;;39421:32:0;;:62;;;;39471:12;:10;:12::i;:::-;-1:-1:-1;;;;;39457:26:0;:10;-1:-1:-1;;;;;39457:26:0;;39421:62;39413:86;;;;-1:-1:-1;;;39413:86:0;;;;;;;:::i;:::-;117297:15:::1;:34:::0;117181:158::o;53969:170::-;39104:6;;-1:-1:-1;;;;;39104:6:0;39090:10;:20;;:65;;-1:-1:-1;39145:10:0;;-1:-1:-1;;;;;39145:10:0;39131;:24;39090:65;:112;;;;39190:12;:10;:12::i;:::-;-1:-1:-1;;;;;39176:26:0;:10;-1:-1:-1;;;;;39176:26:0;;39090:112;:163;;;;39237:5;;;;;;;;;-1:-1:-1;;;;;39237:5:0;-1:-1:-1;;;;;39237:14:0;;:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;39223:30:0;:10;-1:-1:-1;;;;;39223:30:0;;39090:163;:216;;;;39288:5;;;;;;;;;-1:-1:-1;;;;;39288:5:0;-1:-1:-1;;;;;39288:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;39274:32:0;:10;-1:-1:-1;;;;;39274:32:0;;39090:216;39068:277;;;;-1:-1:-1;;;39068:277:0;;;;;;;:::i;:::-;54107:5:::1;::::0;:23:::1;::::0;;-1:-1:-1;;;54107:23:0;;;;54092:39:::1;::::0;-1:-1:-1;;;;;54107:5:0::1;::::0;:21:::1;::::0;:23:::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;:5;:23:::1;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;54092:14;:39::i;:::-;53969:170::o:0;58430:1613::-;39104:6;;-1:-1:-1;;;;;39104:6:0;39090:10;:20;;:65;;-1:-1:-1;39145:10:0;;-1:-1:-1;;;;;39145:10:0;39131;:24;39090:65;:112;;;;39190:12;:10;:12::i;:::-;-1:-1:-1;;;;;39176:26:0;:10;-1:-1:-1;;;;;39176:26:0;;39090:112;:163;;;;39237:5;;;;;;;;;-1:-1:-1;;;;;39237:5:0;-1:-1:-1;;;;;39237:14:0;;:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;39223:30:0;:10;-1:-1:-1;;;;;39223:30:0;;39090:163;:216;;;;39288:5;;;;;;;;;-1:-1:-1;;;;;39288:5:0;-1:-1:-1;;;;;39288:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;39274:32:0;:10;-1:-1:-1;;;;;39274:32:0;;39090:216;39068:277;;;;-1:-1:-1;;;39068:277:0;;;;;;;:::i;:::-;58481:14:::1;58510:12:::0;58537:23:::1;58563:5;;;;;;;;;-1:-1:-1::0;;;;;58563:5:0::1;-1:-1:-1::0;;;;;58563:21:0::1;;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;58635:13;::::0;58537:49;;-1:-1:-1;58597:19:0::1;::::0;58635:13:::1;;58631:571;;;58717:19;58739:23;:21;:23::i;:::-;58717:45;;58795:15;58781:11;:29;58777:220;;;58838:29;58856:11:::0;58838:15;:29:::1;:::i;:::-;58831:36;;58777:220;;;58907:15;58893:11;:29;58889:108;;;58952:29;58966:15:::0;58952:11;:29:::1;:::i;:::-;58943:38;;58889:108;59025:22;59043:4:::0;59025:15;:22:::1;:::i;:::-;59011:36;;58650:409;58631:571;;;59160:30;59174:15;59160:13;:30::i;:::-;59130:60:::0;;-1:-1:-1;59130:60:0;-1:-1:-1;59130:60:0;-1:-1:-1;58631:571:0::1;59418:5;::::0;:31:::1;::::0;-1:-1:-1;;;59418:31:0;;59443:4:::1;59418:31;::::0;::::1;2092:51:1::0;59398:17:0::1;::::0;-1:-1:-1;;;;;59418:5:0::1;::::0;:16:::1;::::0;2065:18:1;;59418:31:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:41;;::::0;59488:5:::1;::::0;:39:::1;::::0;-1:-1:-1;;;59488:39:0;;::::1;::::0;::::1;14407:25:1::0;;;14448:18;;;14441:34;;;14491:18;;;14484:34;;;59418:41:0;;-1:-1:-1;;;;;;59488:5:0::1;::::0;:12:::1;::::0;14380:18:1;;59488:39:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;59470:57;;59603:31;59618:15;59603:14;:31::i;:::-;59689:13;::::0;::::1;;:42:::0;::::1;;;-1:-1:-1::0;59706:11:0::1;::::0;::::1;::::0;::::1;-1:-1:-1::0;;;;;59706:11:0::1;:25:::0;::::1;59689:42;59685:280;;;59768:11;::::0;59756:85:::1;::::0;-1:-1:-1;;;59756:85:0;;::::1;::::0;::::1;14788:25:1::0;;;14829:18;;;14822:34;;;14872:18;;;14865:34;;;14915:18;;;14908:34;;;14958:19;;;14951:35;;;59768:11:0::1;::::0;;::::1;-1:-1:-1::0;;;;;59768:11:0::1;::::0;59756:30:::1;::::0;14760:19:1;;59756:85:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;59748:110;;;::::0;-1:-1:-1;;;59748:110:0;;15449:2:1;59748:110:0::1;::::0;::::1;15431:21:1::0;15488:2;15468:18;;;15461:30;-1:-1:-1;;;15507:18:1;;;15500:42;15559:18;;59748:110:0::1;15247:336:1::0;59748:110:0::1;59685:280;;;59896:22;::::0;59913:4:::1;2684:41:1::0;;59896:22:0::1;::::0;2672:2:1;2657:18;59896:22:0::1;;;;;;;59949:4;59933:20:::0;;-1:-1:-1;;59933:20:0::1;::::0;::::1;::::0;;59685:280:::1;59982:53;::::0;;15819:25:1;;;15875:2;15860:18;;15853:34;;;15903:18;;;15896:34;;;15961:2;15946:18;;15939:34;;;59982:53:0::1;::::0;15806:3:1;15791:19;59982:53:0::1;;;;;;;58470:1573;;;;;58430:1613::o:0;116861:150::-;39435:5;;;;;;;;;-1:-1:-1;;;;;39435:5:0;-1:-1:-1;;;;;39435:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;39421:32:0;:10;-1:-1:-1;;;;;39421:32:0;;:62;;;;39471:12;:10;:12::i;:::-;-1:-1:-1;;;;;39457:26:0;:10;-1:-1:-1;;;;;39457:26:0;;39421:62;39413:86;;;;-1:-1:-1;;;39413:86:0;;;;;;;:::i;:::-;116973:13:::1;:30:::0;116861:150::o;42270:174::-;38463:10;;-1:-1:-1;;;;;38463:10:0;38449;:24;;:54;;;38491:12;:10;:12::i;:::-;-1:-1:-1;;;;;38477:26:0;:10;-1:-1:-1;;;;;38477:26:0;;38449:54;38441:78;;;;-1:-1:-1;;;38441:78:0;;;;;;;:::i;:::-;-1:-1:-1;;;;;42349:21:0;::::1;42341:30;;;::::0;::::1;;42382:6;:16:::0;;-1:-1:-1;;;;;;42382:16:0::1;-1:-1:-1::0;;;;;42382:16:0;::::1;::::0;;::::1;::::0;;;42414:22:::1;::::0;2092:51:1;;;42414:22:0::1;::::0;2080:2:1;2065:18;42414:22:0::1;1933:216:1::0;45948:171:0;38463:10;;-1:-1:-1;;;;;38463:10:0;38449;:24;;:54;;;38491:12;:10;:12::i;:::-;-1:-1:-1;;;;;38477:26:0;:10;-1:-1:-1;;;;;38477:26:0;;38449:54;38441:78;;;;-1:-1:-1;;;38441:78:0;;;;;;;:::i;:::-;46037:11:::1;:26;46051:12:::0;;46037:11;:26:::1;:::i;:::-;;46079:32;46098:12;;46079:32;;;;;;;:::i;:::-;;;;;;;;45948:171:::0;;:::o;117019:154::-;39435:5;;;;;;;;;-1:-1:-1;;;;;39435:5:0;-1:-1:-1;;;;;39435:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;39421:32:0;:10;-1:-1:-1;;;;;39421:32:0;;:62;;;;39471:12;:10;:12::i;:::-;-1:-1:-1;;;;;39457:26:0;:10;-1:-1:-1;;;;;39457:26:0;;39421:62;39413:86;;;;-1:-1:-1;;;39413:86:0;;;;;;;:::i;:::-;117133:14:::1;:32:::0;117019:154::o;108161:133::-;108245:5;;:31;;-1:-1:-1;;;108245:31:0;;108270:4;108245:31;;;2092:51:1;108218:7:0;;-1:-1:-1;;;;;108245:5:0;;:16;;2065:18:1;;108245:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:41;;;108238:48;;108161:133;:::o;44744:169::-;38463:10;;-1:-1:-1;;;;;38463:10:0;38449;:24;;:54;;;38491:12;:10;:12::i;:::-;-1:-1:-1;;;;;38477:26:0;:10;-1:-1:-1;;;;;38477:26:0;;38449:54;38441:78;;;;-1:-1:-1;;;38441:78:0;;;;;;;:::i;:::-;44827:12:::1;:28:::0;;;44871:34:::1;::::0;1528:25:1;;;44871:34:0::1;::::0;1516:2:1;1501:18;44871:34:0::1;1382:177:1::0;129675:1484:0;130128:10;;130088:19;;130128:10;;130120:29;;;;-1:-1:-1;;;130120:29:0;;18639:2:1;130120:29:0;;;18621:21:1;18678:1;18658:18;;;18651:29;-1:-1:-1;;;18696:18:1;;;18689:36;18742:18;;130120:29:0;18437:329:1;130120:29:0;130300:4;130294:11;-1:-1:-1;;;130319:135:0;;130199:4;130183:22;;130491:4;130475:21;;130468:43;;;-1:-1:-1;;;130566:4:0;130550:21;;130525:146;130183:22;130722:4;130294:11;130160:20;130700:27;130685:42;;;130777:11;-1:-1:-1;;;;;130750:50:0;;130815:6;130836:11;130862:8;130885:7;130907:9;130931:14;130960:15;130990:16;131021;131052:15;131082:21;130750:364;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;131132:19:0;;-1:-1:-1;;;;;131132:19:0;;;-1:-1:-1;131132:19:0;;-1:-1:-1;131132:19:0;;;130109:1050;129675:1484;;;;;;;;;;;;;:::o;128931:736::-;129359:51;129371:6;129379:11;129392:8;129402:7;129359:11;:51::i;:::-;129421:238;129452:9;129476:14;129505:15;129535:16;129566;129597:15;129627:21;129421:16;:238::i;:::-;128931:736;;;;;;;;;;;:::o;41110:171::-;39435:5;;;;;;;;;-1:-1:-1;;;;;39435:5:0;-1:-1:-1;;;;;39435:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;39421:32:0;:10;-1:-1:-1;;;;;39421:32:0;;:62;;;;39471:12;:10;:12::i;:::-;-1:-1:-1;;;;;39457:26:0;:10;-1:-1:-1;;;;;39457:26:0;;39421:62;39413:86;;;;-1:-1:-1;;;39413:86:0;;;;;;;:::i;:::-;41200:32:::1;::::0;2709:14:1;;2702:22;2684:41;;41200:32:0::1;::::0;2672:2:1;2657:18;41200:32:0::1;;;;;;;41243:13;:30:::0;;-1:-1:-1;;41243:30:0::1;::::0;::::1;;::::0;;;::::1;::::0;;41110:171::o;41514:202::-;38463:10;;-1:-1:-1;;;;;38463:10:0;38449;:24;;:54;;;38491:12;:10;:12::i;:::-;-1:-1:-1;;;;;38477:26:0;:10;-1:-1:-1;;;;;38477:26:0;;38449:54;38441:78;;;;-1:-1:-1;;;38441:78:0;;;;;;;:::i;:::-;-1:-1:-1;;;;;41601:25:0;::::1;41593:34;;;::::0;::::1;;41638:10;:24:::0;;-1:-1:-1;;;;;;41638:24:0::1;-1:-1:-1::0;;;;;41638:24:0;::::1;::::0;;::::1;::::0;;;41678:30:::1;::::0;2092:51:1;;;41678:30:0::1;::::0;2080:2:1;2065:18;41678:30:0::1;1933:216:1::0;61574:281:0;61663:5;;-1:-1:-1;;;;;61663:5:0;61641:10;:28;61633:37;;;;;;61727:5;;61689:34;;;-1:-1:-1;;;61689:34:0;;;;-1:-1:-1;;;;;61727:5:0;;;;61689:32;;;;;:34;;;;;;;;;;;;;;:32;:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;61689:43:0;;61681:52;;;;;;61744:30;61761:12;61744:16;:30::i;:::-;61817:4;;:29;;-1:-1:-1;;;61817:29:0;;61840:4;61817:29;;;2092:51:1;61785:62:0;;61803:12;;-1:-1:-1;;;;;61817:4:0;;;;:14;;2065:18:1;;61817:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;61785:4;;-1:-1:-1;;;;;61785:4:0;;:62;:17;:62::i;:::-;61574:281;:::o;109519:377::-;109580:7;109600:26;109665:12;;;;;;;;;-1:-1:-1;;;;;109665:12:0;-1:-1:-1;;;;;109665:20:0;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;109630:18;:11;109644:4;109630:18;:::i;:::-;109629:58;;;;:::i;:::-;109811:4;;109859:12;;109600:87;;-1:-1:-1;109718:170:0;;109600:87;;-1:-1:-1;;;;;109811:4:0;;;;109859:12;109718:14;:170::i;124307:32::-;;;;;;;;;;;;108577:549;108667:8;;:33;;-1:-1:-1;;;108667:33:0;;108694:4;108667:33;;;2092:51:1;108628:7:0;;;;-1:-1:-1;;;;;108667:8:0;;;;:18;;2065::1;;108667:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;108648:52;;108717:8;;;;;;;;;-1:-1:-1;;;;;108717:8:0;-1:-1:-1;;;;;108717:20:0;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;108743:1;108717:27;108713:134;;108834:1;108827:8;;;108577:549;:::o;108713:134::-;108882:8;;:24;;;-1:-1:-1;;;108882:24:0;;;;108857:22;;-1:-1:-1;;;;;108882:8:0;;:22;;:24;;;;;;;;;;;;;;:8;:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;108857:49;;108917:17;108960:12;;;;;;;;;-1:-1:-1;;;;;108960:12:0;-1:-1:-1;;;;;108937:46:0;;:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;108917:68;;;-1:-1:-1;109102:15:0;108917:68;109102:2;:15;:::i;:::-;109072:25;109083:14;109072:8;:25;:::i;:::-;109071:47;;;;:::i;:::-;109064:54;;;;;108577:549;:::o;42733:269::-;38865:10;;-1:-1:-1;;;;;38865:10:0;38851;:24;38843:48;;;;-1:-1:-1;;;38843:48:0;;23025:2:1;38843:48:0;;;23007:21:1;23064:2;23044:18;;;23037:30;-1:-1:-1;;;23083:18:1;;;23076:41;23134:18;;38843:48:0;22823:335:1;38843:48:0;-1:-1:-1;;;;;42814:22:0;::::1;42806:31;;;::::0;::::1;;42848:5;::::0;42862:7:::1;::::0;;42848:25:::1;::::0;-1:-1:-1;;;42848:25:0;;-1:-1:-1;;;;;42862:7:0;;::::1;42848:25:::0;;::::1;23345:51:1::0;;;;42848:5:0::1;23412:18:1::0;;;23405:34;42848:5:0;;::::1;::::0;:13:::1;::::0;23318:18:1;;42848:25:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;42884:7:0::1;:18:::0;;-1:-1:-1;;;;;;42884:18:0::1;-1:-1:-1::0;;;;;42884:18:0;;::::1;::::0;;::::1;::::0;;42913:5:::1;::::0;:41:::1;::::0;-1:-1:-1;;;42913:41:0;;;;::::1;23345:51:1::0;;;;-1:-1:-1;;23412:18:1;;;23405:34;42913:5:0::1;::::0;:13:::1;::::0;23318:18:1;;42913:41:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;42970:24:0::1;::::0;-1:-1:-1;;;;;2110:32:1;;2092:51;;42970:24:0::1;::::0;2080:2:1;2065:18;42970:24:0::1;1933:216:1::0;55906:1718:0;55982:4;;;56084:5;;:31;;-1:-1:-1;;;56084:31:0;;56109:4;56084:31;;;2092:51:1;55999:43:0;;-1:-1:-1;56053:28:0;;-1:-1:-1;;;;;56084:5:0;;;;:16;;2065:18:1;;56084:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;56053:62;;56192:6;:17;;;56213:1;56192:22;56188:40;;-1:-1:-1;56223:5:0;;55906:1718;-1:-1:-1;;;55906:1718:0:o;56188:40::-;56370:14;;56350:17;;;;56332:35;;:15;:35;:::i;:::-;:52;56328:70;;;-1:-1:-1;56393:5:0;;55906:1718;-1:-1:-1;;;55906:1718:0:o;56328:70::-;56514:14;;56493:17;;;;56475:35;;:15;:35;:::i;:::-;:53;56471:70;;-1:-1:-1;56537:4:0;;55906:1718;-1:-1:-1;;;55906:1718:0:o;56471:70::-;56971:5;;:23;;;-1:-1:-1;;;56971:23:0;;;;56949:19;;-1:-1:-1;;;;;56971:5:0;;:21;;:23;;;;;;;;;;;;;;:5;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;56949:45;;57023:13;;57009:11;:27;57005:44;;;-1:-1:-1;57045:4:0;;55906:1718;-1:-1:-1;;;;55906:1718:0:o;57005:44::-;57103:13;57119:22;:20;:22::i;:::-;57103:38;;57228:6;:16;;;57212:13;;57204:5;:21;;;;:::i;:::-;:40;57200:57;;;-1:-1:-1;57253:4:0;;55906:1718;-1:-1:-1;;;;;55906:1718:0:o;57200:57::-;57270:14;57311:6;:16;;;57303:5;:24;57299:63;;;57346:16;;;;57338:24;;:5;:24;:::i;:::-;57329:33;;57299:63;57532:5;;:23;;;-1:-1:-1;;;57532:23:0;;;;57515:14;;-1:-1:-1;;;;;57532:5:0;;:21;;:23;;;;;;;;;;;;;;:5;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;57515:40;-1:-1:-1;57600:15:0;57609:6;57515:40;57600:15;:::i;:::-;57589:8;57574:12;;:23;;;;:::i;:::-;:41;;55906:1718;-1:-1:-1;;;;;;;;55906:1718:0:o;108302:267::-;108364:7;108384:24;108464:20;:18;:20::i;:::-;108411:12;;:37;;-1:-1:-1;;;108411:37:0;;108442:4;108411:37;;;2092:51:1;-1:-1:-1;;;;;108411:12:0;;;;:22;;2065:18:1;;108411:37:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:73;;;;:::i;:::-;108384:100;;108534:27;108544:16;108534:9;:27::i;:::-;108502:4;;:29;;-1:-1:-1;;;108502:29:0;;108525:4;108502:29;;;2092:51:1;-1:-1:-1;;;;;108502:4:0;;;;:14;;2065:18:1;;108502:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:59;;;;:::i;:::-;108495:66;;;108302:267;:::o;44190:154::-;38463:10;;-1:-1:-1;;;;;38463:10:0;38449;:24;;:54;;;38491:12;:10;:12::i;:::-;-1:-1:-1;;;;;38477:26:0;:10;-1:-1:-1;;;;;38477:26:0;;38449:54;38441:78;;;;-1:-1:-1;;;38441:78:0;;;;;;;:::i;:::-;44268:14:::1;:23:::0;;;44307:29:::1;::::0;1528:25:1;;;44307:29:0::1;::::0;1516:2:1;1501:18;44307:29:0::1;1382:177:1::0;62284:173:0;38629:10;;-1:-1:-1;;;;;38629:10:0;38615;:24;;:54;;;38657:12;:10;:12::i;:::-;-1:-1:-1;;;;;38643:26:0;:10;-1:-1:-1;;;;;38643:26:0;;38615:54;:88;;;;38687:5;;;;;;;;;-1:-1:-1;;;;;38687:5:0;-1:-1:-1;;;;;38687:14:0;;:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;38673:30:0;:10;-1:-1:-1;;;;;38673:30:0;;38615:88;:124;;;;38721:5;;;;;;;;;-1:-1:-1;;;;;38721:5:0;-1:-1:-1;;;;;38721:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;38707:32:0;:10;-1:-1:-1;;;;;38707:32:0;;38615:124;38593:185;;;;-1:-1:-1;;;38593:185:0;;;;;;;:::i;:::-;62356:13:::1;:20:::0;;-1:-1:-1;;62356:20:0::1;62372:4;62356:20;::::0;;62387:5:::1;::::0;:22:::1;::::0;;-1:-1:-1;;;62387:22:0;;;;-1:-1:-1;;;;;62387:5:0;;::::1;::::0;:20:::1;::::0;:22:::1;::::0;;::::1;::::0;62356:13:::1;::::0;62387:22;;;;;;;;62356:13;62387:5;:22;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;62427:22:0::1;::::0;::::1;::::0;-1:-1:-1;62427:22:0;;-1:-1:-1;62427:22:0::1;62284:173::o:0;124412:30::-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;124412:30:0;;-1:-1:-1;124412:30:0;:::o;25746:616::-;26110:10;;;26109:62;;-1:-1:-1;26126:39:0;;-1:-1:-1;;;26126:39:0;;26150:4;26126:39;;;24071:34:1;-1:-1:-1;;;;;24141:15:1;;;24121:18;;;24114:43;26126:15:0;;;;;24006:18:1;;26126:39:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:44;26109:62;26087:166;;;;-1:-1:-1;;;26087:166:0;;24370:2:1;26087:166:0;;;24352:21:1;24409:2;24389:18;;;24382:30;24448:34;24428:18;;;24421:62;-1:-1:-1;;;24499:18:1;;;24492:52;24561:19;;26087:166:0;24168:418:1;26087:166:0;26291:62;;-1:-1:-1;;;;;23363:32:1;;26291:62:0;;;23345:51:1;23412:18;;;23405:34;;;26264:90:0;;26284:5;;-1:-1:-1;;;26314:22:0;23318:18:1;;26291:62:0;;;;-1:-1:-1;;26291:62:0;;;;;;;;;;;;;;-1:-1:-1;;;;;26291:62:0;-1:-1:-1;;;;;;26291:62:0;;;;;;;;;;26264:19;:90::i;:::-;25746:616;;;:::o;19959:229::-;20096:12;20128:52;20150:6;20158:4;20164:1;20167:12;20128:21;:52::i;:::-;20121:59;19959:229;-1:-1:-1;;;;19959:229:0:o;46273:98::-;46345:5;;:18;;;-1:-1:-1;;;46345:18:0;;;;46318:7;;-1:-1:-1;;;;;46345:5:0;;:16;;:18;;;;;;;;;;;;;;:5;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;25010:211::-;25154:58;;-1:-1:-1;;;;;23363:32:1;;25154:58:0;;;23345:51:1;23412:18;;;23405:34;;;25127:86:0;;25147:5;;-1:-1:-1;;;25177:23:0;23318:18:1;;25154:58:0;23163:282:1;109904:423:0;110054:15;110082;110100:10;-1:-1:-1;;;;;110100:19:0;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;110082:39;;;;110132:13;110148:8;-1:-1:-1;;;;;110148:17:0;;:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;110132:35;;;;110206:7;110198:5;:15;:121;;110302:15;110312:5;110302:7;:15;:::i;:::-;110297:21;;:2;:21;:::i;:::-;110286:33;;:7;:33;:::i;:::-;110198:121;;;110249:15;110257:7;110249:5;:15;:::i;:::-;110244:21;;:2;:21;:::i;:::-;110233:33;;:7;:33;:::i;:::-;110178:141;109904:423;-1:-1:-1;;;;;;109904:423:0:o;112460:592::-;112646:4;;:29;;-1:-1:-1;;;112646:29:0;;112669:4;112646:29;;;2092:51:1;112564:25:0;;;;;;-1:-1:-1;;;;;112646:4:0;;:14;;2065:18:1;;112646:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;112622:53;;112708:13;112692;:29;112688:177;;;112738:19;112760:29;112776:13;112760;:29;:::i;:::-;112738:51;;112828:25;112841:11;112828:12;:25::i;:::-;-1:-1:-1;112804:49:0;-1:-1:-1;;112688:177:0;112897:95;112920:13;112948:33;112968:13;112948:17;:33;:::i;:::-;112897:8;:95::i;:::-;112877:115;-1:-1:-1;113011:33:0;112877:115;113011:13;:33;:::i;:::-;113003:41;;112611:441;112460:592;;;:::o;110462:490::-;110566:4;;:29;;-1:-1:-1;;;110566:29:0;;110589:4;110566:29;;;2092:51:1;110541:22:0;;-1:-1:-1;;;;;110566:4:0;;:14;;2065:18:1;;110566:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;110541:54;;110627:16;110610:14;:33;110606:339;;;110660:23;110686:33;110703:16;110686:14;:33;:::i;:::-;110660:59;;110752:42;110761:15;110778;;110752:8;:42::i;:::-;110734:60;;110809:43;110836:15;110809:26;:43::i;:::-;110867:8;;;;;;;;;-1:-1:-1;;;;;110867:8:0;-1:-1:-1;;;;;110867:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;110918:15:0;110900;:33;110530:422;110462:490;:::o;115236:359::-;115419:8;;115437:33;;-1:-1:-1;;;115437:33:0;;115464:4;115437:33;;;2092:51:1;115323:20:0;;-1:-1:-1;;;;;115419:8:0;;:17;;:8;;115437:18;;2065::1;;115437:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;115419:52;;;;;;;;;;;;;1528:25:1;;1516:2;1501:18;;1382:177;115419:52:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;115502:12:0;;:37;;-1:-1:-1;;;115502:37:0;;115533:4;115502:37;;;2092:51:1;115482:58:0;;-1:-1:-1;;;;;115502:12:0;;:22;;2065:18:1;;115502:37:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;115482:19;:58::i;:::-;115558:4;;:29;;-1:-1:-1;;;115558:29:0;;115581:4;115558:29;;;2092:51:1;-1:-1:-1;;;;;115558:4:0;;;;:14;;2065:18:1;;115558:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;110960:1492::-;111248:5;;:31;;-1:-1:-1;;;111248:31:0;;111273:4;111248:31;;;2092:51:1;111077:15:0;;;;111198:16;;111077:15;;-1:-1:-1;;;;;111248:5:0;;;;:16;;2065:18:1;;111248:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:41;;;111227:62;;111300:20;111323:22;:20;:22::i;:::-;111300:45;;111377:10;111362:12;:25;111358:159;;;111414:25;111429:10;111414:12;:25;:::i;:::-;111404:35;;111358:159;;;111480:25;111493:12;111480:10;:25;:::i;:::-;111472:33;;111358:159;111529:27;111559:22;111574:7;111559:12;:22;:::i;:::-;111616:4;;:29;;-1:-1:-1;;;111616:29:0;;111639:4;111616:29;;;2092:51:1;111529:52:0;;-1:-1:-1;111592:21:0;;-1:-1:-1;;;;;111616:4:0;;;;:14;;2065:18:1;;111616:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;111592:53;;111684:13;111662:19;:35;111658:787;;;111714:19;111736:35;111758:13;111736:19;:35;:::i;:::-;111714:57;;111791:23;111818:25;111831:11;111818:12;:25::i;:::-;111788:55;;;111880:7;111862:15;:25;111858:215;;;111918:25;111928:15;111918:7;:25;:::i;:::-;111908:35;;111858:215;;;112001:25;112019:7;112001:15;:25;:::i;:::-;111992:35;;:5;:35;:::i;:::-;111984:43;;112056:1;112046:11;;111858:215;112105:4;;:29;;-1:-1:-1;;;112105:29:0;;112128:4;112105:29;;;2092:51:1;-1:-1:-1;;;;;112105:4:0;;;;:14;;2065:18:1;;112105:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;112089:45;;112209:7;112193:13;:23;112189:245;;;112247:13;112237:23;;112294:1;112279:16;;112189:245;;;112337:22;112352:7;112337:12;:22;:::i;:::-;112321:13;:38;112317:117;;;112395:23;112411:7;112395:13;:23;:::i;:::-;112380:38;;112317:117;111699:746;;111658:787;111172:1280;;;;110960:1492;;;;;:::o;40193:739::-;40365:4;;-1:-1:-1;;;;;40365:4:0;40357:27;40349:68;;;;-1:-1:-1;;;40349:68:0;;24793:2:1;40349:68:0;;;24775:21:1;24832:2;24812:18;;;24805:30;24871;24851:18;;;24844:58;24919:18;;40349:68:0;24591:352:1;40349:68:0;40430:5;:24;;-1:-1:-1;;;;;;40430:24:0;-1:-1:-1;;;;;40430:24:0;;;;;;;;40479:13;;;-1:-1:-1;;;40479:13:0;;;;:11;;:13;;;;;;;;;;;;;;;40430:24;40479:13;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;40465:4;:28;;-1:-1:-1;;;;;;40465:28:0;-1:-1:-1;;;;;40465:28:0;;;;;;;;;40504:43;;40521:6;-1:-1:-1;;40504:16:0;:43::i;:::-;40606:10;:24;;-1:-1:-1;;;;;40606:24:0;;;-1:-1:-1;;;;;;40606:24:0;;;;;;;40641:7;:18;;;;;;;;;;;;40670:6;:16;;;;;;;;;;;;;;-1:-1:-1;40732:14:0;:18;;;40778:5;40761:14;:22;40809:3;40794:12;:18;40823:13;:17;40853:5;;:41;;-1:-1:-1;;;40853:41:0;;;;;23345:51:1;;;;-1:-1:-1;;23412:18:1;;;23405:34;40853:5:0;;;:13;;23318:18:1;;40853:41:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;40193:739;;;;:::o;126280:2643::-;126891:8;:28;;-1:-1:-1;;;;;;126891:28:0;-1:-1:-1;;;;;126891:28:0;;;;;;;;126961:16;;;-1:-1:-1;;;126961:16:0;;;;:14;;:16;;;;;;;;;;;;;;;126891:28;126961:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;126932:12;:46;;-1:-1:-1;;;;;;126932:46:0;-1:-1:-1;;;;;126932:46:0;;;;;;;;;127007:24;;;-1:-1:-1;;;127007:24:0;;;;-1:-1:-1;;126932:46:0;127007:22;;:24;;;;;;;;;;;;;;126932:46;127007:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;127042:14;:24;;;127079:13;:30;;;127120:14;:32;;;127163:15;:34;;;127210:4;;126989:42;;-1:-1:-1;127210:59:0;;-1:-1:-1;;;;;127210:4:0;107978:42;-1:-1:-1;;127210:16:0;:59::i;:::-;127344:8;;127295:12;;127280:116;;-1:-1:-1;;;;;127295:12:0;;;;127344:8;-1:-1:-1;;127280:41:0;:116::i;:::-;127409:34;;;;:15;;:34;;;;;:::i;:::-;-1:-1:-1;127454:32:0;;;;:14;;:32;;;;;:::i;:::-;-1:-1:-1;127497:44:0;;;;:20;;:44;;;;;:::i;:::-;-1:-1:-1;127857:15:0;:22;127832;127890:858;127912:14;127908:1;:18;;;127890:858;;;127948:14;127986:323;;;;;;;;128037:15;128053:1;128037:18;;;;;;;;;;:::i;:::-;;;;;;;;;127986:323;;;;128088:20;128109:1;128088:23;;;;;;;;;;:::i;:::-;;;;;;;;;127986:323;;;;128150:20;128171:1;128175;128171:5;;;;:::i;:::-;128150:27;;;;;;;;;;:::i;:::-;;;;;;;;;127986:323;;;;128217:1;127986:323;;;;128276:1;128265:13;;;;;;;3958:4:1;3946:17;;;;3928:36;;3916:2;3901:18;;3786:184;128265:13:0;;;;-1:-1:-1;;128265:13:0;;;;;;;;;127986:323;;127948:376;;;;;;;;-1:-1:-1;127948:376:0;;;128265:13;127948:376;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;128341:21;128386:299;;;;;;;;128437:15;128474:1;128453:22;;128470:1;128453:14;:18;;;;:::i;:::-;:22;;;;:::i;:::-;128437:39;;;;;;;;:::i;:::-;;;;;;;;;128386:299;;;;128499:20;128537:1;128520:18;;:14;:18;;;;:::i;:::-;128499:40;;;;;;;;:::i;:::-;;;;;;;;;128386:299;;;;128562:20;128604:1;128583:22;;128600:1;128583:14;:18;;;;:::i;:::-;:22;;;;:::i;:::-;128562:44;;;;;;;;:::i;:::-;;;;;;;;;128386:299;;;;128629:1;128386:299;;;;128664:1;128653:13;;;;;;;3958:4:1;3946:17;;;;3928:36;;3916:2;3901:18;;3786:184;128653:13:0;;;;-1:-1:-1;;128653:13:0;;;;;;;;;128386:299;;128341:359;;;;;;;;-1:-1:-1;128341:359:0;;;128653:13;128341:359;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;128717:6:0;:19;;;;;;;-1:-1:-1;128717:19:0;;;;-1:-1:-1;;;128717:19:0;;;;;-1:-1:-1;127928:3:0;;;;:::i;:::-;;;;127890:858;;;-1:-1:-1;;128760:6:0;:19;;;;;;;;-1:-1:-1;128760:19:0;;;;-1:-1:-1;;;128760:19:0;;;;;128890:18;:25;;-1:-1:-1;;128890:25:0;;;;;;-1:-1:-1;;;;;;;;126280:2643:0:o;116220:205::-;116351:8;;116383:33;;-1:-1:-1;;;116383:33:0;;116410:4;116383:33;;;2092:51:1;-1:-1:-1;;;;;116351:8:0;;;;:17;;116369:12;;116351:8;;116383:18;;2065::1;;116383:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;116351:66;;-1:-1:-1;;;;;;116351:66:0;;;;;;;-1:-1:-1;;;;;23363:32:1;;;116351:66:0;;;23345:51:1;23412:18;;;23405:34;23318:18;;116351:66:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;27583:716::-;28007:23;28033:69;28061:4;28033:69;;;;;;;;;;;;;;;;;28041:5;-1:-1:-1;;;;;28033:27:0;;;:69;;;;;:::i;:::-;28117:17;;28007:95;;-1:-1:-1;28117:21:0;28113:179;;28214:10;28203:30;;;;;;;;;;;;:::i;:::-;28195:85;;;;-1:-1:-1;;;28195:85:0;;27226:2:1;28195:85:0;;;27208:21:1;27265:2;27245:18;;;27238:30;27304:34;27284:18;;;27277:62;-1:-1:-1;;;27355:18:1;;;27348:40;27405:19;;28195:85:0;27024:406:1;21079:510:0;21249:12;21307:5;21282:21;:30;;21274:81;;;;-1:-1:-1;;;21274:81:0;;27637:2:1;21274:81:0;;;27619:21:1;27676:2;27656:18;;;27649:30;27715:34;27695:18;;;27688:62;-1:-1:-1;;;27766:18:1;;;27759:36;27812:19;;21274:81:0;27435:402:1;21274:81:0;17476:20;;21366:60;;;;-1:-1:-1;;;21366:60:0;;28044:2:1;21366:60:0;;;28026:21:1;28083:2;28063:18;;;28056:30;28122:31;28102:18;;;28095:59;28171:18;;21366:60:0;27842:353:1;21366:60:0;21440:12;21454:23;21481:6;-1:-1:-1;;;;;21481:11:0;21500:5;21507:4;21481:31;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21439:73;;;;21530:51;21547:7;21556:10;21568:12;21530:16;:51::i;:::-;21523:58;21079:510;-1:-1:-1;;;;;;;21079:510:0:o;113176:2052::-;113348:4;;:29;;-1:-1:-1;;;113348:29:0;;113371:4;113348:29;;;2092:51:1;113261:25:0;;;;;;-1:-1:-1;;;;;113348:4:0;;:14;;2065:18:1;;113348:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;113319:58;;113390:18;113411:28;113421:17;113411:9;:28::i;:::-;113521:12;;:37;;-1:-1:-1;;;113521:37:0;;113552:4;113521:37;;;2092:51:1;113390:49:0;;-1:-1:-1;113493:25:0;;-1:-1:-1;;;;;113521:12:0;;;;:22;;2065:18:1;;113521:37:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;113493:65;;113571:22;113596:8;;;;;;;;;-1:-1:-1;;;;;113596:8:0;-1:-1:-1;;;;;113596:22:0;;:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;113571:49;-1:-1:-1;113631:29:0;113571:49;113664:17;:10;113677:4;113664:17;:::i;:::-;113663:36;;;;:::i;:::-;113741:8;;:33;;-1:-1:-1;;;113741:33:0;;113768:4;113741:33;;;2092:51:1;113631:68:0;;-1:-1:-1;113712:26:0;;-1:-1:-1;;;;;113741:8:0;;;;:18;;2065::1;;113741:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;113712:62;;113815:18;113791:21;:42;113787:352;;;113931:18;-1:-1:-1;113931:18:0;113966:28;114057:4;113998:55;114039:14;113931:18;113998:55;:::i;:::-;113997:64;;;;:::i;:::-;113966:95;;114096:31;114106:20;114096:9;:31::i;:::-;114076:51;;113835:304;113787:352;114155:25;;114151:508;;114197:8;;:40;;-1:-1:-1;;;114197:40:0;;;;;1528:25:1;;;-1:-1:-1;;;;;114197:8:0;;;;:17;;1501:18:1;;114197:40:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;114256:18:0;;;;114252:396;;;114543:25;114567:1;114543:21;:25;:::i;:::-;114481:8;;:33;;-1:-1:-1;;;114481:33:0;;114508:4;114481:33;;;2092:51:1;-1:-1:-1;;;;;114481:8:0;;;;:18;;2065::1;;114481:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;114460:54;;:18;:54;:::i;:::-;:108;;114430:202;;;;-1:-1:-1;;;114430:202:0;;28694:2:1;114430:202:0;;;28676:21:1;28733:2;28713:18;;;28706:30;-1:-1:-1;;;28752:18:1;;;28745:50;28812:18;;114430:202:0;28492:344:1;114430:202:0;114698:12;;:37;;-1:-1:-1;;;114698:37:0;;114729:4;114698:37;;;2092:51:1;114671:24:0;;114751:17;;-1:-1:-1;;;;;114698:12:0;;;;:22;;2065:18:1;;114698:37:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:70;;;;:::i;:::-;114671:97;-1:-1:-1;114785:20:0;;114781:90;;114822:37;114842:16;114822:19;:37::i;:::-;114910:4;;:29;;-1:-1:-1;;;114910:29:0;;114933:4;114910:29;;;2092:51:1;114883:24:0;;114955:18;;-1:-1:-1;;;;;114910:4:0;;;;:14;;2065:18:1;;114910:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:63;;;;:::i;:::-;114883:90;;115010:17;114990:16;:37;114986:235;;115064:17;115044:37;;114986:235;;;115134:16;-1:-1:-1;115134:16:0;115173:36;115134:16;115173:17;:36;:::i;:::-;115165:44;;114986:235;113308:1920;;;;;;;;113176:2052;;;:::o;66282:106::-;66340:7;66371:1;66367;:5;:13;;66379:1;66367:13;;;66375:1;66367:13;66360:20;;66282:106;;;;;:::o;131282:776::-;131510:11;131483:14;131498:1;131483:17;;;;;;;;:::i;:::-;;;;;;;;;:24;:17;;;;;:24;;;;:38;;;;131580:246;;;;;;;;131650:4;131580:246;;;;;;;;;;;;;;;;;;;;;;131839:211;-1:-1:-1;;;131839:211:0;;107978:42;;131839:23;;:211;;131483:17;131924:14;;131953;;131580:246;;132003:6;;132024:15;;131839:211;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;131839:211:0;;;;;;;;;;;;:::i;132066:666::-;132178:10;132144:21;132166:1;132144:24;;;;;;;;:::i;:::-;;;;;;;;;:31;:24;;;;;:31;;;;:44;;;;132247:246;;;;;;;;132317:4;132247:246;;;;;;;;;;;;;;;;;;;;;;132506:218;-1:-1:-1;;;132506:218:0;;107978:42;;132506:23;;:218;;132144:24;132591:21;;132627:14;;132247:246;;132677:6;;132698:15;;132506:218;;;:::i;23765:712::-;23915:12;23944:7;23940:530;;;-1:-1:-1;23975:10:0;23968:17;;23940:530;24089:17;;:21;24085:374;;24287:10;24281:17;24348:15;24335:10;24331:2;24327:19;24320:44;24085:374;24430:12;24423:20;;-1:-1:-1;;;24423:20:0;;;;;;;;:::i;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14:131:1;-1:-1:-1;;;;;89:31:1;;79:42;;69:70;;135:1;132;125:12;150:134;218:20;;247:31;218:20;247:31;:::i;:::-;150:134;;;:::o;289:247::-;348:6;401:2;389:9;380:7;376:23;372:32;369:52;;;417:1;414;407:12;369:52;456:9;443:23;475:31;500:5;475:31;:::i;541:250::-;626:1;636:113;650:6;647:1;644:13;636:113;;;726:11;;;720:18;707:11;;;700:39;672:2;665:10;636:113;;;-1:-1:-1;;783:1:1;765:16;;758:27;541:250::o;796:396::-;945:2;934:9;927:21;908:4;977:6;971:13;1020:6;1015:2;1004:9;1000:18;993:34;1036:79;1108:6;1103:2;1092:9;1088:18;1083:2;1075:6;1071:15;1036:79;:::i;:::-;1176:2;1155:15;-1:-1:-1;;1151:29:1;1136:45;;;;1183:2;1132:54;;796:396;-1:-1:-1;;796:396:1:o;1197:180::-;1256:6;1309:2;1297:9;1288:7;1284:23;1280:32;1277:52;;;1325:1;1322;1315:12;1277:52;-1:-1:-1;1348:23:1;;1197:180;-1:-1:-1;1197:180:1:o;1564:118::-;1650:5;1643:13;1636:21;1629:5;1626:32;1616:60;;1672:1;1669;1662:12;1687:241;1743:6;1796:2;1784:9;1775:7;1771:23;1767:32;1764:52;;;1812:1;1809;1802:12;1764:52;1851:9;1838:23;1870:28;1892:5;1870:28;:::i;2959:592::-;3030:6;3038;3091:2;3079:9;3070:7;3066:23;3062:32;3059:52;;;3107:1;3104;3097:12;3059:52;3147:9;3134:23;3176:18;3217:2;3209:6;3206:14;3203:34;;;3233:1;3230;3223:12;3203:34;3271:6;3260:9;3256:22;3246:32;;3316:7;3309:4;3305:2;3301:13;3297:27;3287:55;;3338:1;3335;3328:12;3287:55;3378:2;3365:16;3404:2;3396:6;3393:14;3390:34;;;3420:1;3417;3410:12;3390:34;3465:7;3460:2;3451:6;3447:2;3443:15;3439:24;3436:37;3433:57;;;3486:1;3483;3476:12;3433:57;3517:2;3509:11;;;;;3539:6;;-1:-1:-1;2959:592:1;;-1:-1:-1;;;;2959:592:1:o;3975:127::-;4036:10;4031:3;4027:20;4024:1;4017:31;4067:4;4064:1;4057:15;4091:4;4088:1;4081:15;4107:252;4179:2;4173:9;4221:3;4209:16;;4255:18;4240:34;;4276:22;;;4237:62;4234:88;;;4302:18;;:::i;:::-;4338:2;4331:22;4107:252;:::o;4364:275::-;4435:2;4429:9;4500:2;4481:13;;-1:-1:-1;;4477:27:1;4465:40;;4535:18;4520:34;;4556:22;;;4517:62;4514:88;;;4582:18;;:::i;:::-;4618:2;4611:22;4364:275;;-1:-1:-1;4364:275:1:o;4644:183::-;4704:4;4737:18;4729:6;4726:30;4723:56;;;4759:18;;:::i;:::-;-1:-1:-1;4804:1:1;4800:14;4816:4;4796:25;;4644:183::o;4832:662::-;4886:5;4939:3;4932:4;4924:6;4920:17;4916:27;4906:55;;4957:1;4954;4947:12;4906:55;4993:6;4980:20;5019:4;5043:60;5059:43;5099:2;5059:43;:::i;:::-;5043:60;:::i;:::-;5137:15;;;5223:1;5219:10;;;;5207:23;;5203:32;;;5168:12;;;;5247:15;;;5244:35;;;5275:1;5272;5265:12;5244:35;5311:2;5303:6;5299:15;5323:142;5339:6;5334:3;5331:15;5323:142;;;5405:17;;5393:30;;5443:12;;;;5356;;5323:142;;;-1:-1:-1;5483:5:1;4832:662;-1:-1:-1;;;;;;4832:662:1:o;5499:745::-;5561:5;5614:3;5607:4;5599:6;5595:17;5591:27;5581:55;;5632:1;5629;5622:12;5581:55;5668:6;5655:20;5694:4;5718:60;5734:43;5774:2;5734:43;:::i;5718:60::-;5812:15;;;5898:1;5894:10;;;;5882:23;;5878:32;;;5843:12;;;;5922:15;;;5919:35;;;5950:1;5947;5940:12;5919:35;5986:2;5978:6;5974:15;5998:217;6014:6;6009:3;6006:15;5998:217;;;6094:3;6081:17;6111:31;6136:5;6111:31;:::i;:::-;6155:18;;6193:12;;;;6031;;5998:217;;6249:1410;6488:6;6496;6504;6512;6520;6528;6536;6544;6552;6560;6568:7;6622:3;6610:9;6601:7;6597:23;6593:33;6590:53;;;6639:1;6636;6629:12;6590:53;6662:29;6681:9;6662:29;:::i;:::-;6652:39;;6710:38;6744:2;6733:9;6729:18;6710:38;:::i;:::-;6700:48;;6767:38;6801:2;6790:9;6786:18;6767:38;:::i;:::-;6757:48;;6824:38;6858:2;6847:9;6843:18;6824:38;:::i;:::-;6814:48;;6881:39;6915:3;6904:9;6900:19;6881:39;:::i;:::-;6871:49;;6967:3;6956:9;6952:19;6939:33;6929:43;;7019:3;7008:9;7004:19;6991:33;6981:43;;7071:3;7060:9;7056:19;7043:33;7033:43;;7095:18;7163:2;7156:3;7145:9;7141:19;7128:33;7125:41;7122:61;;;7179:1;7176;7169:12;7122:61;7202:88;7282:7;7274:3;7263:9;7259:19;7246:33;7235:9;7231:49;7202:88;:::i;:::-;7192:98;;7340:2;7333:3;7322:9;7318:19;7305:33;7302:41;7299:61;;;7356:1;7353;7346:12;7299:61;7379:96;7467:7;7459:3;7448:9;7444:19;7431:33;7420:9;7416:49;7379:96;:::i;:::-;7369:106;;7525:2;7518:3;7507:9;7503:19;7490:33;7487:41;7484:61;;;7541:1;7538;7531:12;7484:61;;7565:88;7645:7;7637:3;7626:9;7622:19;7609:33;7598:9;7594:49;7565:88;:::i;:::-;7554:99;;6249:1410;;;;;;;;;;;;;;:::o;8112:335::-;8314:2;8296:21;;;8353:2;8333:18;;;8326:30;-1:-1:-1;;;8387:2:1;8372:18;;8365:41;8438:2;8423:18;;8112:335::o;9120:127::-;9181:10;9176:3;9172:20;9169:1;9162:31;9212:4;9209:1;9202:15;9236:4;9233:1;9226:15;9591:127;9652:10;9647:3;9643:20;9640:1;9633:31;9683:4;9680:1;9673:15;9707:4;9704:1;9697:15;9723:135;9762:3;9783:17;;;9780:43;;9803:18;;:::i;:::-;-1:-1:-1;9850:1:1;9839:13;;9723:135::o;9863:184::-;9933:6;9986:2;9974:9;9965:7;9961:23;9957:32;9954:52;;;10002:1;9999;9992:12;9954:52;-1:-1:-1;10025:16:1;;9863:184;-1:-1:-1;9863:184:1:o;10052:380::-;10131:1;10127:12;;;;10174;;;10195:61;;10249:4;10241:6;10237:17;10227:27;;10195:61;10302:2;10294:6;10291:14;10271:18;10268:38;10265:161;;10348:10;10343:3;10339:20;10336:1;10329:31;10383:4;10380:1;10373:15;10411:4;10408:1;10401:15;10437:719;10517:6;10570:2;10558:9;10549:7;10545:23;10541:32;10538:52;;;10586:1;10583;10576:12;10538:52;10619:9;10613:16;10648:18;10689:2;10681:6;10678:14;10675:34;;;10705:1;10702;10695:12;10675:34;10743:6;10732:9;10728:22;10718:32;;10788:7;10781:4;10777:2;10773:13;10769:27;10759:55;;10810:1;10807;10800:12;10759:55;10839:2;10833:9;10861:2;10857;10854:10;10851:36;;;10867:18;;:::i;:::-;10909:53;10952:2;10933:13;;-1:-1:-1;;10929:27:1;10958:2;10925:36;10909:53;:::i;:::-;10896:66;;10985:2;10978:5;10971:17;11025:7;11020:2;11015;11011;11007:11;11003:20;11000:33;10997:53;;;11046:1;11043;11036:12;10997:53;11059:67;11123:2;11118;11111:5;11107:14;11102:2;11098;11094:11;11059:67;:::i;:::-;-1:-1:-1;11145:5:1;10437:719;-1:-1:-1;;;;10437:719:1:o;11161:1132::-;-1:-1:-1;;;11716:3:1;11709:19;11691:3;11757:6;11751:13;11773:74;11840:6;11836:1;11831:3;11827:11;11820:4;11812:6;11808:17;11773:74;:::i;:::-;11875:6;11870:3;11866:16;11856:26;;-1:-1:-1;;;11932:2:1;11928:1;11924:2;11920:10;11913:22;11966:6;11960:13;11982:75;12048:8;12044:1;12040:2;12036:10;12029:4;12021:6;12017:17;11982:75;:::i;:::-;12117:1;12076:17;;12109:10;;;12102:22;12149:13;;12171:75;12149:13;12233:1;12225:10;;12218:4;12206:17;;12171:75;:::i;:::-;12266:17;12285:1;12262:25;;11161:1132;-1:-1:-1;;;;;11161:1132:1:o;12298:251::-;12368:6;12421:2;12409:9;12400:7;12396:23;12392:32;12389:52;;;12437:1;12434;12427:12;12389:52;12469:9;12463:16;12488:31;12513:5;12488:31;:::i;12554:168::-;12594:7;12660:1;12656;12652:6;12648:14;12645:1;12642:21;12637:1;12630:9;12623:17;12619:45;12616:71;;;12667:18;;:::i;:::-;-1:-1:-1;12707:9:1;;12554:168::o;12727:217::-;12767:1;12793;12783:132;;12837:10;12832:3;12828:20;12825:1;12818:31;12872:4;12869:1;12862:15;12900:4;12897:1;12890:15;12783:132;-1:-1:-1;12929:9:1;;12727:217::o;12949:784::-;13051:6;13104:3;13092:9;13083:7;13079:23;13075:33;13072:53;;;13121:1;13118;13111:12;13072:53;13147:22;;:::i;:::-;13198:9;13192:16;13185:5;13178:31;13262:2;13251:9;13247:18;13241:25;13236:2;13229:5;13225:14;13218:49;13320:2;13309:9;13305:18;13299:25;13294:2;13287:5;13283:14;13276:49;13378:2;13367:9;13363:18;13357:25;13352:2;13345:5;13341:14;13334:49;13437:3;13426:9;13422:19;13416:26;13410:3;13403:5;13399:15;13392:51;13497:3;13486:9;13482:19;13476:26;13470:3;13463:5;13459:15;13452:51;13557:3;13546:9;13542:19;13536:26;13530:3;13523:5;13519:15;13512:51;13617:3;13606:9;13602:19;13596:26;13590:3;13583:5;13579:15;13572:51;13642:3;13698:2;13687:9;13683:18;13677:25;13672:2;13665:5;13661:14;13654:49;;13722:5;13712:15;;;12949:784;;;;:::o;14072:128::-;14139:9;;;14160:11;;;14157:37;;;14174:18;;:::i;14997:245::-;15064:6;15117:2;15105:9;15096:7;15092:23;15088:32;15085:52;;;15133:1;15130;15123:12;15085:52;15165:9;15159:16;15184:28;15206:5;15184:28;:::i;16110:545::-;16212:2;16207:3;16204:11;16201:448;;;16248:1;16273:5;16269:2;16262:17;16318:4;16314:2;16304:19;16388:2;16376:10;16372:19;16369:1;16365:27;16359:4;16355:38;16424:4;16412:10;16409:20;16406:47;;;-1:-1:-1;16447:4:1;16406:47;16502:2;16497:3;16493:12;16490:1;16486:20;16480:4;16476:31;16466:41;;16557:82;16575:2;16568:5;16565:13;16557:82;;;16620:17;;;16601:1;16590:13;16557:82;;;16561:3;;;16110:545;;;:::o;16831:1206::-;16955:18;16950:3;16947:27;16944:53;;;16977:18;;:::i;:::-;17006:94;17096:3;17056:38;17088:4;17082:11;17056:38;:::i;:::-;17050:4;17006:94;:::i;:::-;17126:1;17151:2;17146:3;17143:11;17168:1;17163:616;;;;17823:1;17840:3;17837:93;;;-1:-1:-1;17896:19:1;;;17883:33;17837:93;-1:-1:-1;;16788:1:1;16784:11;;;16780:24;16776:29;16766:40;16812:1;16808:11;;;16763:57;17943:78;;17136:895;;17163:616;16057:1;16050:14;;;16094:4;16081:18;;-1:-1:-1;;17199:17:1;;;17300:9;17322:229;17336:7;17333:1;17330:14;17322:229;;;17425:19;;;17412:33;17397:49;;17532:4;17517:20;;;;17485:1;17473:14;;;;17352:12;17322:229;;;17326:3;17579;17570:7;17567:16;17564:159;;;17703:1;17699:6;17693:3;17687;17684:1;17680:11;17676:21;17672:34;17668:39;17655:9;17650:3;17646:19;17633:33;17629:79;17621:6;17614:95;17564:159;;;17766:1;17760:3;17757:1;17753:11;17749:19;17743:4;17736:33;17136:895;;16831:1206;;;:::o;18042:390::-;18201:2;18190:9;18183:21;18240:6;18235:2;18224:9;18220:18;18213:34;18297:6;18289;18284:2;18273:9;18269:18;18256:48;18353:1;18324:22;;;18348:2;18320:31;;;18313:42;;;;18416:2;18395:15;;;-1:-1:-1;;18391:29:1;18376:45;18372:54;;18042:390;-1:-1:-1;18042:390:1:o;18771:435::-;18824:3;18862:5;18856:12;18889:6;18884:3;18877:19;18915:4;18944:2;18939:3;18935:12;18928:19;;18981:2;18974:5;18970:14;19002:1;19012:169;19026:6;19023:1;19020:13;19012:169;;;19087:13;;19075:26;;19121:12;;;;19156:15;;;;19048:1;19041:9;19012:169;;;-1:-1:-1;19197:3:1;;18771:435;-1:-1:-1;;;;;18771:435:1:o;19211:1682::-;19749:4;19778:3;19817:1;19813;19808:3;19804:11;19800:19;19858:2;19850:6;19846:15;19835:9;19828:34;19881:2;19931;19923:6;19919:15;19914:2;19903:9;19899:18;19892:43;19983:2;19975:6;19971:15;19966:2;19955:9;19951:18;19944:43;20035:2;20027:6;20023:15;20018:2;20007:9;20003:18;19996:43;20088:2;20080:6;20076:15;20070:3;20059:9;20055:19;20048:44;20129:6;20123:3;20112:9;20108:19;20101:35;20173:6;20167:3;20156:9;20152:19;20145:35;20217:6;20211:3;20200:9;20196:19;20189:35;20261:2;20255:3;20244:9;20240:19;20233:31;20287:56;20339:2;20328:9;20324:18;20316:6;20287:56;:::i;:::-;20380:22;;;20374:3;20359:19;;20352:51;20452:13;;20474:22;;;20550:15;;;;-1:-1:-1;20512:15:1;;;;20583:1;20593:178;20607:6;20604:1;20601:13;20593:178;;;20672:13;;20668:22;;20656:35;;20746:15;;;;20711:12;;;;20629:1;20622:9;20593:178;;;20597:3;;20817:9;20812:3;20808:19;20802:3;20791:9;20787:19;20780:48;20845:42;20883:3;20874:7;20845:42;:::i;:::-;20837:50;;;;;;19211:1682;;;;;;;;;;;;;;:::o;21171:273::-;21239:6;21292:2;21280:9;21271:7;21267:23;21263:32;21260:52;;;21308:1;21305;21298:12;21260:52;21340:9;21334:16;21390:4;21383:5;21379:16;21372:5;21369:27;21359:55;;21410:1;21407;21400:12;21449:422;21538:1;21581:5;21538:1;21595:270;21616:7;21606:8;21603:21;21595:270;;;21675:4;21671:1;21667:6;21663:17;21657:4;21654:27;21651:53;;;21684:18;;:::i;:::-;21734:7;21724:8;21720:22;21717:55;;;21754:16;;;;21717:55;21833:22;;;;21793:15;;;;21595:270;;;21599:3;21449:422;;;;;:::o;21876:806::-;21925:5;21955:8;21945:80;;-1:-1:-1;21996:1:1;22010:5;;21945:80;22044:4;22034:76;;-1:-1:-1;22081:1:1;22095:5;;22034:76;22126:4;22144:1;22139:59;;;;22212:1;22207:130;;;;22119:218;;22139:59;22169:1;22160:10;;22183:5;;;22207:130;22244:3;22234:8;22231:17;22228:43;;;22251:18;;:::i;:::-;-1:-1:-1;;22307:1:1;22293:16;;22322:5;;22119:218;;22421:2;22411:8;22408:16;22402:3;22396:4;22393:13;22389:36;22383:2;22373:8;22370:16;22365:2;22359:4;22356:12;22352:35;22349:77;22346:159;;;-1:-1:-1;22458:19:1;;;22490:5;;22346:159;22537:34;22562:8;22556:4;22537:34;:::i;:::-;22607:6;22603:1;22599:6;22595:19;22586:7;22583:32;22580:58;;;22618:18;;:::i;:::-;22656:20;;21876:806;-1:-1:-1;;;21876:806:1:o;22687:131::-;22747:5;22776:36;22803:8;22797:4;22776:36;:::i;23729:125::-;23794:9;;;23815:10;;;23812:36;;;23828:18;;:::i;25137:148::-;25225:4;25204:12;;;25218;;;25200:31;;25243:13;;25240:39;;;25259:18;;:::i;25489:1350::-;25613:3;25607:10;25640:18;25632:6;25629:30;25626:56;;;25662:18;;:::i;:::-;25691:97;25781:6;25741:38;25773:4;25767:11;25741:38;:::i;:::-;25735:4;25691:97;:::i;:::-;25843:4;;25907:2;25896:14;;25924:1;25919:663;;;;26626:1;26643:6;26640:89;;;-1:-1:-1;26695:19:1;;;26689:26;26640:89;-1:-1:-1;;16788:1:1;16784:11;;;16780:24;16776:29;16766:40;16812:1;16808:11;;;16763:57;26742:81;;25889:944;;25919:663;16057:1;16050:14;;;16094:4;16081:18;;-1:-1:-1;;25955:20:1;;;26073:236;26087:7;26084:1;26081:14;26073:236;;;26176:19;;;26170:26;26155:42;;26268:27;;;;26236:1;26224:14;;;;26103:19;;26073:236;;;26077:3;26337:6;26328:7;26325:19;26322:201;;;26398:19;;;26392:26;-1:-1:-1;;26481:1:1;26477:14;;;26493:3;26473:24;26469:37;26465:42;26450:58;26435:74;;26322:201;-1:-1:-1;;;;;26569:1:1;26553:14;;;26549:22;26536:36;;-1:-1:-1;25489:1350:1:o;26844:175::-;26881:3;26925:4;26918:5;26914:16;26954:4;26945:7;26942:17;26939:43;;26962:18;;:::i;:::-;27011:1;26998:15;;26844:175;-1:-1:-1;;26844:175:1:o;28200:287::-;28329:3;28367:6;28361:13;28383:66;28442:6;28437:3;28430:4;28422:6;28418:17;28383:66;:::i;:::-;28465:16;;;;;28200:287;-1:-1:-1;;28200:287:1:o;28841:503::-;28910:3;28948:5;28942:12;28975:6;28970:3;28963:19;29001:4;29030:2;29025:3;29021:12;29014:19;;29052:5;29049:1;29042:16;29094:2;29091:1;29081:16;29115:1;29125:194;29139:6;29136:1;29133:13;29125:194;;;29204:13;;-1:-1:-1;;;;;29200:39:1;29188:52;;29260:12;;;;29236:1;29295:14;;;;29154:9;29125:194;;29709:468;29769:3;29807:5;29801:12;29834:6;29829:3;29822:19;29860:4;29889:2;29884:3;29880:12;29873:19;;29911:5;29908:1;29901:16;29953:2;29950:1;29940:16;29974:1;29984:168;29998:6;29995:1;29992:13;29984:168;;;30059:13;;30047:26;;30093:12;;;;30140:1;30128:14;;;;30013:9;29984:168;;30182:2868;30702:4;30750:3;30739:9;30735:19;30784:1;30776:6;30773:13;30763:144;;30829:10;30824:3;30820:20;30817:1;30810:31;30864:4;30861:1;30854:15;30892:4;30889:1;30882:15;30763:144;30934:6;30923:9;30916:25;30977:3;30972:2;30961:9;30957:18;30950:31;31001:6;31036;31030:13;31067:6;31059;31052:22;31105:3;31094:9;31090:19;31083:26;;31168:3;31158:6;31155:1;31151:14;31140:9;31136:30;31132:40;31118:54;;31191:1;31212:6;31208:2;31201:18;31256:2;31252;31242:17;31277:2;31288:1369;31302:6;31299:1;31296:13;31288:1369;;;31395:3;31391:8;31379:9;31371:6;31367:22;31363:37;31358:3;31351:50;31435:6;31429:13;31421:6;31414:29;31498:1;31490:6;31486:14;31480:21;31475:2;31467:6;31463:15;31456:46;31559:1;31551:6;31547:14;31541:21;31534:4;31526:6;31522:17;31515:48;31620:4;31612:6;31608:17;31602:24;31595:4;31587:6;31583:17;31576:51;31672:4;31664:6;31660:17;31716:4;31709;31701:6;31697:17;31690:31;31745:2;31783:12;31777:19;31825:36;31851:9;31825:36;:::i;:::-;31900:8;31893:4;31885:6;31881:17;31874:35;31944:1;31933:9;31929:17;31964:1;31959:168;;;;32145:1;32140:412;;;;31922:630;;31959:168;32028:3;32024:8;32013:9;32009:24;32003:3;31995:6;31991:16;31984:50;32109:3;32095:8;32088:16;32081:24;32078:1;32074:32;32066:6;32062:45;32058:55;32051:62;;31959:168;;32140:412;32176:12;32172:2;32165:24;32235:2;32231;32221:17;32266:2;32285:203;32301:8;32296:3;32293:17;32285:203;;;32408:14;;32384:16;;;32402:3;32380:26;32373:50;32468:1;32455:15;;;;32329:2;32320:12;32285:203;;;32516:16;;32534:3;32512:26;;-1:-1:-1;;31922:630:1;-1:-1:-1;32575:3:1;;-1:-1:-1;;;32644:2:1;32635:12;;;;;-1:-1:-1;32613:1:1;32601:14;;;;;31324:1;31317:9;31288:1369;;;31292:3;;;;;32707:9;32699:6;32695:22;32688:4;32677:9;32673:20;32666:52;32741:60;32794:6;32786;32741:60;:::i;:::-;29471:12;;-1:-1:-1;;;;;29467:21:1;;;32866:4;32851:20;;;29455:34;;;;29552:4;29541:16;;29535:23;29528:31;29521:39;29505:14;;;29498:63;29614:4;29603:16;;29597:23;29593:32;;;29577:14;;;29570:56;29678:16;;29672:23;29665:31;29658:39;29642:14;;;29635:63;32727:74;-1:-1:-1;32921:9:1;32913:6;32909:22;32903:3;32892:9;32888:19;32881:51;32949;32993:6;32985;32949:51;:::i;:::-;32941:59;;;33037:6;33031:3;33020:9;33016:19;33009:35;30182:2868;;;;;;;;;:::o;33055:880::-;33149:6;33180:2;33223;33211:9;33202:7;33198:23;33194:32;33191:52;;;33239:1;33236;33229:12;33191:52;33272:9;33266:16;33305:18;33297:6;33294:30;33291:50;;;33337:1;33334;33327:12;33291:50;33360:22;;33413:4;33405:13;;33401:27;-1:-1:-1;33391:55:1;;33442:1;33439;33432:12;33391:55;33471:2;33465:9;33494:60;33510:43;33550:2;33510:43;:::i;33494:60::-;33588:15;;;33670:1;33666:10;;;;33658:19;;33654:28;;;33619:12;;;;33694:19;;;33691:39;;;33726:1;33723;33716:12;33691:39;33750:11;;;;33770:135;33786:6;33781:3;33778:15;33770:135;;;33852:10;;33840:23;;33803:12;;;;33883;;;;33770:135;
Swarm Source
ipfs://4562973348774e80883b9d16ad72d2c136a329109f479e6341b87c41b892133d
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
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.