Contract Overview
Balance:
0 AVAX
AVAX Value:
$0.00
[ Download CSV Export ]
Contract Source Code Verified (Exact Match)
Contract Name:
StrategyTJLiquidityBookLB
Compiler Version
v0.8.17+commit.8df45f5f
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/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: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, 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}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, 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}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, 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) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, 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) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * 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: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer( address from, address to, uint256 amount ) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, 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; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _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; // Overflow not possible: amount <= accountBalance <= totalSupply. _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 Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance( address owner, address spender, uint256 amount ) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - 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 {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; 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)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://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 functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../LB/LBErrors.sol"; /** LiquidityAmounts Errors */ error LiquidityAmounts__LengthMismatch();
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../LB/libraries/Math512Bits.sol"; import "../LB/libraries/BinHelper.sol"; import "../LB/libraries/Constants.sol"; import "../LB/libraries/SafeCast.sol"; import "../LB/interfaces/ILBPair.sol"; import "../LB/interfaces/ILBToken.sol"; import "./JoeV2PeripheryErrors.sol"; /// @title Liquidity Book periphery library for Liquidity Amount /// @author Trader Joe /// @notice Periphery library to help compute liquidity amounts from amounts and ids. /// @dev The caller must ensure that the parameters are valid following the comments. library LiquidityAmounts { using Math512Bits for uint256; using SafeCast for uint256; /// @notice Return the liquidities amounts received for a given amount of tokenX and tokenY /// @dev The caller needs to ensure that the ids are unique, if not, the result will be wrong. /// @param ids the list of ids where the user want to add liquidity /// @param binStep the binStep of the pair /// @param amountX the amount of tokenX /// @param amountY the amount of tokenY /// @return liquidities the amounts of liquidity received function getLiquiditiesForAmounts( uint256[] memory ids, uint16 binStep, uint112 amountX, uint112 amountY ) internal pure returns (uint256[] memory liquidities) { liquidities = new uint256[](ids.length); for (uint256 i; i < ids.length; ++i) { uint256 price = BinHelper.getPriceFromId(ids[i].safe24(), binStep); liquidities[i] = price.mulShiftRoundDown(amountX, Constants.SCALE_OFFSET) + amountY; } } /// @notice Return the amounts of token received for a given amount of liquidities /// @dev The different arrays needs to use the same binId for each index /// @param liquidities the list of liquidity amounts for each binId /// @param totalSupplies the list of totalSupply for each binId /// @param binReservesX the list of reserve of token X for each binId /// @param binReservesY the list of reserve of token Y for each binId /// @return amountX the amount of tokenX received by the user /// @return amountY the amount of tokenY received by the user function getAmountsForLiquidities( uint256[] memory liquidities, uint256[] memory totalSupplies, uint112[] memory binReservesX, uint112[] memory binReservesY ) internal pure returns (uint256 amountX, uint256 amountY) { if ( liquidities.length != totalSupplies.length && liquidities.length != binReservesX.length && liquidities.length != binReservesY.length ) revert LiquidityAmounts__LengthMismatch(); for (uint256 i; i < liquidities.length; ++i) { amountX += liquidities[i].mulDivRoundDown(binReservesX[i], totalSupplies[i]); amountY += liquidities[i].mulDivRoundDown(binReservesY[i], totalSupplies[i]); } } /// @notice Return the ids and liquidities of an user /// @dev The caller needs to ensure that the ids are unique, if not, the result will be wrong. /// @param user The address of the user /// @param ids the list of ids where the user have liquidity /// @param LBPair The address of the LBPair /// @return liquidities the list of amount of liquidity of the user function getLiquiditiesOf( address user, uint256[] memory ids, address LBPair ) internal view returns (uint256[] memory liquidities) { liquidities = new uint256[](ids.length); for (uint256 i; i < ids.length; ++i) { liquidities[i] = ILBToken(LBPair).balanceOf(user, ids[i].safe24()); } } /// @notice Return the amounts received by an user if he were to burn all its liquidity /// @dev The caller needs to ensure that the ids are unique, if not, the result will be wrong. /// @param user The address of the user /// @param ids the list of ids where the user would remove its liquidity, ids need to be in ascending order to assert uniqueness /// @param LBPair The address of the LBPair /// @return amountX the amount of tokenX received by the user /// @return amountY the amount of tokenY received by the user function getAmountsOf( address user, uint256[] memory ids, address LBPair ) internal view returns (uint256 amountX, uint256 amountY) { for (uint256 i; i < ids.length; ++i) { uint24 id = ids[i].safe24(); uint256 liquidity = ILBToken(LBPair).balanceOf(user, id); (uint256 binReserveX, uint256 binReserveY) = ILBPair(LBPair).getBin(id); uint256 totalSupply = ILBToken(LBPair).totalSupply(id); amountX += liquidity.mulDivRoundDown(binReserveX, totalSupply); amountY += liquidity.mulDivRoundDown(binReserveY, totalSupply); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./interfaces/ILBPair.sol"; /** LBRouter errors */ error LBRouter__SenderIsNotWAVAX(); error LBRouter__PairNotCreated(address tokenX, address tokenY, uint256 binStep); error LBRouter__WrongAmounts(uint256 amount, uint256 reserve); error LBRouter__SwapOverflows(uint256 id); error LBRouter__BrokenSwapSafetyCheck(); error LBRouter__NotFactoryOwner(); error LBRouter__TooMuchTokensIn(uint256 excess); error LBRouter__BinReserveOverflows(uint256 id); error LBRouter__IdOverflows(int256 id); error LBRouter__LengthsMismatch(); error LBRouter__WrongTokenOrder(); error LBRouter__IdSlippageCaught(uint256 activeIdDesired, uint256 idSlippage, uint256 activeId); error LBRouter__AmountSlippageCaught(uint256 amountXMin, uint256 amountX, uint256 amountYMin, uint256 amountY); error LBRouter__IdDesiredOverflows(uint256 idDesired, uint256 idSlippage); error LBRouter__FailedToSendAVAX(address recipient, uint256 amount); error LBRouter__DeadlineExceeded(uint256 deadline, uint256 currentTimestamp); error LBRouter__AmountSlippageBPTooBig(uint256 amountSlippage); error LBRouter__InsufficientAmountOut(uint256 amountOutMin, uint256 amountOut); error LBRouter__MaxAmountInExceeded(uint256 amountInMax, uint256 amountIn); error LBRouter__InvalidTokenPath(address wrongToken); error LBRouter__InvalidVersion(uint256 version); error LBRouter__WrongAvaxLiquidityParameters( address tokenX, address tokenY, uint256 amountX, uint256 amountY, uint256 msgValue ); /** LBToken errors */ error LBToken__SpenderNotApproved(address owner, address spender); error LBToken__TransferFromOrToAddress0(); error LBToken__MintToAddress0(); error LBToken__BurnFromAddress0(); error LBToken__BurnExceedsBalance(address from, uint256 id, uint256 amount); error LBToken__LengthMismatch(uint256 accountsLength, uint256 idsLength); error LBToken__SelfApproval(address owner); error LBToken__TransferExceedsBalance(address from, uint256 id, uint256 amount); error LBToken__TransferToSelf(); /** LBFactory errors */ error LBFactory__IdenticalAddresses(IERC20 token); error LBFactory__QuoteAssetNotWhitelisted(IERC20 quoteAsset); error LBFactory__QuoteAssetAlreadyWhitelisted(IERC20 quoteAsset); error LBFactory__AddressZero(); error LBFactory__LBPairAlreadyExists(IERC20 tokenX, IERC20 tokenY, uint256 _binStep); error LBFactory__LBPairNotCreated(IERC20 tokenX, IERC20 tokenY, uint256 binStep); error LBFactory__DecreasingPeriods(uint16 filterPeriod, uint16 decayPeriod); error LBFactory__ReductionFactorOverflows(uint16 reductionFactor, uint256 max); error LBFactory__VariableFeeControlOverflows(uint16 variableFeeControl, uint256 max); error LBFactory__BaseFeesBelowMin(uint256 baseFees, uint256 minBaseFees); error LBFactory__FeesAboveMax(uint256 fees, uint256 maxFees); error LBFactory__FlashLoanFeeAboveMax(uint256 fees, uint256 maxFees); error LBFactory__BinStepRequirementsBreached(uint256 lowerBound, uint16 binStep, uint256 higherBound); error LBFactory__ProtocolShareOverflows(uint16 protocolShare, uint256 max); error LBFactory__FunctionIsLockedForUsers(address user); error LBFactory__FactoryLockIsAlreadyInTheSameState(); error LBFactory__LBPairIgnoredIsAlreadyInTheSameState(); error LBFactory__BinStepHasNoPreset(uint256 binStep); error LBFactory__SameFeeRecipient(address feeRecipient); error LBFactory__SameFlashLoanFee(uint256 flashLoanFee); error LBFactory__LBPairSafetyCheckFailed(address LBPairImplementation); error LBFactory__SameImplementation(address LBPairImplementation); error LBFactory__ImplementationNotSet(); /** LBPair errors */ error LBPair__InsufficientAmounts(); error LBPair__AddressZero(); error LBPair__AddressZeroOrThis(); error LBPair__CompositionFactorFlawed(uint256 id); error LBPair__InsufficientLiquidityMinted(uint256 id); error LBPair__InsufficientLiquidityBurned(uint256 id); error LBPair__WrongLengths(); error LBPair__OnlyStrictlyIncreasingId(); error LBPair__OnlyFactory(); error LBPair__DistributionsOverflow(); error LBPair__OnlyFeeRecipient(address feeRecipient, address sender); error LBPair__OracleNotEnoughSample(); error LBPair__AlreadyInitialized(); error LBPair__OracleNewSizeTooSmall(uint256 newSize, uint256 oracleSize); error LBPair__FlashLoanCallbackFailed(); error LBPair__FlashLoanInvalidBalance(); error LBPair__FlashLoanInvalidToken(); /** BinHelper errors */ error BinHelper__BinStepOverflows(uint256 bp); error BinHelper__IdOverflows(); /** Math128x128 errors */ error Math128x128__PowerUnderflow(uint256 x, int256 y); error Math128x128__LogUnderflow(); /** Math512Bits errors */ error Math512Bits__MulDivOverflow(uint256 prod1, uint256 denominator); error Math512Bits__ShiftDivOverflow(uint256 prod1, uint256 denominator); error Math512Bits__MulShiftOverflow(uint256 prod1, uint256 offset); error Math512Bits__OffsetOverflows(uint256 offset); /** Oracle errors */ error Oracle__AlreadyInitialized(uint256 _index); error Oracle__LookUpTimestampTooOld(uint256 _minTimestamp, uint256 _lookUpTimestamp); error Oracle__NotInitialized(); /** PendingOwnable errors */ error PendingOwnable__NotOwner(); error PendingOwnable__NotPendingOwner(); error PendingOwnable__PendingOwnerAlreadySet(); error PendingOwnable__NoPendingOwner(); error PendingOwnable__AddressZero(); /** ReentrancyGuardUpgradeable errors */ error ReentrancyGuardUpgradeable__ReentrantCall(); error ReentrancyGuardUpgradeable__AlreadyInitialized(); /** SafeCast errors */ error SafeCast__Exceeds256Bits(uint256 x); error SafeCast__Exceeds248Bits(uint256 x); error SafeCast__Exceeds240Bits(uint256 x); error SafeCast__Exceeds232Bits(uint256 x); error SafeCast__Exceeds224Bits(uint256 x); error SafeCast__Exceeds216Bits(uint256 x); error SafeCast__Exceeds208Bits(uint256 x); error SafeCast__Exceeds200Bits(uint256 x); error SafeCast__Exceeds192Bits(uint256 x); error SafeCast__Exceeds184Bits(uint256 x); error SafeCast__Exceeds176Bits(uint256 x); error SafeCast__Exceeds168Bits(uint256 x); error SafeCast__Exceeds160Bits(uint256 x); error SafeCast__Exceeds152Bits(uint256 x); error SafeCast__Exceeds144Bits(uint256 x); error SafeCast__Exceeds136Bits(uint256 x); error SafeCast__Exceeds128Bits(uint256 x); error SafeCast__Exceeds120Bits(uint256 x); error SafeCast__Exceeds112Bits(uint256 x); error SafeCast__Exceeds104Bits(uint256 x); error SafeCast__Exceeds96Bits(uint256 x); error SafeCast__Exceeds88Bits(uint256 x); error SafeCast__Exceeds80Bits(uint256 x); error SafeCast__Exceeds72Bits(uint256 x); error SafeCast__Exceeds64Bits(uint256 x); error SafeCast__Exceeds56Bits(uint256 x); error SafeCast__Exceeds48Bits(uint256 x); error SafeCast__Exceeds40Bits(uint256 x); error SafeCast__Exceeds32Bits(uint256 x); error SafeCast__Exceeds24Bits(uint256 x); error SafeCast__Exceeds16Bits(uint256 x); error SafeCast__Exceeds8Bits(uint256 x); /** TreeMath errors */ error TreeMath__ErrorDepthSearch(); /** JoeLibrary errors */ error JoeLibrary__IdenticalAddresses(); error JoeLibrary__AddressZero(); error JoeLibrary__InsufficientAmount(); error JoeLibrary__InsufficientLiquidity(); /** TokenHelper errors */ error TokenHelper__NonContract(); error TokenHelper__CallFailed(); error TokenHelper__TransferFailed(); /** LBQuoter errors */ error LBQuoter_InvalidLength();
// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.17; /// @title Joe V1 Factory Interface /// @notice Interface to interact with Joe V1 Factory interface IJoeFactory { event PairCreated(address indexed token0, address indexed token1, address pair, uint256); function feeTo() external view returns (address); function feeToSetter() external view returns (address); function migrator() external view returns (address); function getPair(address tokenA, address tokenB) external view returns (address pair); function allPairs(uint256) external view returns (address pair); function allPairsLength() external view returns (uint256); function createPair(address tokenA, address tokenB) external returns (address pair); function setFeeTo(address) external; function setFeeToSetter(address) external; function setMigrator(address) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./ILBPair.sol"; import "./IPendingOwnable.sol"; /// @title Liquidity Book Factory Interface /// @author Trader Joe /// @notice Required interface of LBFactory contract interface ILBFactory is IPendingOwnable { /// @dev Structure to store the LBPair information, such as: /// - binStep: The bin step of the LBPair /// - LBPair: The address of the LBPair /// - createdByOwner: Whether the pair was created by the owner of the factory /// - ignoredForRouting: Whether the pair is ignored for routing or not. An ignored pair will not be explored during routes finding struct LBPairInformation { uint16 binStep; ILBPair LBPair; bool createdByOwner; bool ignoredForRouting; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @title Liquidity Book Flashloan Callback Interface /// @author Trader Joe /// @notice Required interface to interact with LB flash loans interface ILBFlashLoanCallback { function LBFlashLoanCallback( address sender, IERC20 token, uint256 amount, uint256 fee, bytes calldata data ) external returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../libraries/FeeHelper.sol"; import "./ILBFactory.sol"; import "./ILBFlashLoanCallback.sol"; /// @title Liquidity Book Pair Interface /// @author Trader Joe /// @notice Required interface of LBPair contract interface ILBPair { /// @dev Structure to store the reserves of bins: /// - reserveX: The current reserve of tokenX of the bin /// - reserveY: The current reserve of tokenY of the bin struct Bin { uint112 reserveX; uint112 reserveY; uint256 accTokenXPerShare; uint256 accTokenYPerShare; } /// @dev Structure to store the information of the pair such as: /// slot0: /// - activeId: The current id used for swaps, this is also linked with the price /// - reserveX: The sum of amounts of tokenX across all bins /// slot1: /// - reserveY: The sum of amounts of tokenY across all bins /// - oracleSampleLifetime: The lifetime of an oracle sample /// - oracleSize: The current size of the oracle, can be increase by users /// - oracleActiveSize: The current active size of the oracle, composed only from non empty data sample /// - oracleLastTimestamp: The current last timestamp at which a sample was added to the circular buffer /// - oracleId: The current id of the oracle /// slot2: /// - feesX: The current amount of fees to distribute in tokenX (total, protocol) /// slot3: /// - feesY: The current amount of fees to distribute in tokenY (total, protocol) struct PairInformation { uint24 activeId; uint136 reserveX; uint136 reserveY; uint16 oracleSampleLifetime; uint16 oracleSize; uint16 oracleActiveSize; uint40 oracleLastTimestamp; uint16 oracleId; FeeHelper.FeesDistribution feesX; FeeHelper.FeesDistribution feesY; } /// @dev Structure to store the debts of users /// - debtX: The tokenX's debt /// - debtY: The tokenY's debt struct Debts { uint256 debtX; uint256 debtY; } /// @dev Structure to store fees: /// - tokenX: The amount of fees of token X /// - tokenY: The amount of fees of token Y struct Fees { uint128 tokenX; uint128 tokenY; } /// @dev Structure to minting informations: /// - amountXIn: The amount of token X sent /// - amountYIn: The amount of token Y sent /// - amountXAddedToPair: The amount of token X that have been actually added to the pair /// - amountYAddedToPair: The amount of token Y that have been actually added to the pair /// - activeFeeX: Fees X currently generated /// - activeFeeY: Fees Y currently generated /// - totalDistributionX: Total distribution of token X. Should be 1e18 (100%) or 0 (0%) /// - totalDistributionY: Total distribution of token Y. Should be 1e18 (100%) or 0 (0%) /// - id: Id of the current working bin when looping on the distribution array /// - amountX: The amount of token X deposited in the current bin /// - amountY: The amount of token Y deposited in the current bin /// - distributionX: Distribution of token X for the current working bin /// - distributionY: Distribution of token Y for the current working bin struct MintInfo { uint256 amountXIn; uint256 amountYIn; uint256 amountXAddedToPair; uint256 amountYAddedToPair; uint256 activeFeeX; uint256 activeFeeY; uint256 totalDistributionX; uint256 totalDistributionY; uint256 id; uint256 amountX; uint256 amountY; uint256 distributionX; uint256 distributionY; } event Swap( address indexed sender, address indexed recipient, uint256 indexed id, bool swapForY, uint256 amountIn, uint256 amountOut, uint256 volatilityAccumulated, uint256 fees ); event FlashLoan( address indexed sender, ILBFlashLoanCallback indexed receiver, IERC20 token, uint256 amount, uint256 fee ); event CompositionFee( address indexed sender, address indexed recipient, uint256 indexed id, uint256 feesX, uint256 feesY ); event DepositedToBin( address indexed sender, address indexed recipient, uint256 indexed id, uint256 amountX, uint256 amountY ); event WithdrawnFromBin( address indexed sender, address indexed recipient, uint256 indexed id, uint256 amountX, uint256 amountY ); event FeesCollected(address indexed sender, address indexed recipient, uint256 amountX, uint256 amountY); event ProtocolFeesCollected(address indexed sender, address indexed recipient, uint256 amountX, uint256 amountY); event OracleSizeIncreased(uint256 previousSize, uint256 newSize); function tokenX() external view returns (IERC20); function tokenY() external view returns (IERC20); function factory() external view returns (ILBFactory); function getReservesAndId() external view returns ( uint256 reserveX, uint256 reserveY, uint256 activeId ); function getGlobalFees() external view returns ( uint128 feesXTotal, uint128 feesYTotal, uint128 feesXProtocol, uint128 feesYProtocol ); function getOracleParameters() external view returns ( uint256 oracleSampleLifetime, uint256 oracleSize, uint256 oracleActiveSize, uint256 oracleLastTimestamp, uint256 oracleId, uint256 min, uint256 max ); function getOracleSampleFrom(uint256 timeDelta) external view returns ( uint256 cumulativeId, uint256 cumulativeAccumulator, uint256 cumulativeBinCrossed ); function feeParameters() external view returns (FeeHelper.FeeParameters memory); function findFirstNonEmptyBinId(uint24 id_, bool sentTokenY) external view returns (uint24 id); function getBin(uint24 id) external view returns (uint256 reserveX, uint256 reserveY); function pendingFees(address account, uint256[] memory ids) external view returns (uint256 amountX, uint256 amountY); function swap(bool sentTokenY, address to) external returns (uint256 amountXOut, uint256 amountYOut); function flashLoan( ILBFlashLoanCallback receiver, IERC20 token, uint256 amount, bytes calldata data ) external; function mint( uint256[] calldata ids, uint256[] calldata distributionX, uint256[] calldata distributionY, address to ) external returns ( uint256 amountXAddedToPair, uint256 amountYAddedToPair, uint256[] memory liquidityMinted ); function burn( uint256[] calldata ids, uint256[] calldata amounts, address to ) external returns (uint256 amountX, uint256 amountY); function increaseOracleLength(uint16 newSize) external; function collectFees(address account, uint256[] calldata ids) external returns (uint256 amountX, uint256 amountY); function collectProtocolFees() external returns (uint128 amountX, uint128 amountY); function setFeesParameters(bytes32 packedFeeParameters) external; function forceDecay() external; function initialize( IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 sampleLifetime, bytes32 packedFeeParameters ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./IJoeFactory.sol"; import "./ILBPair.sol"; import "./ILBToken.sol"; import "./IWAVAX.sol"; /// @title Liquidity Book Router Interface /// @author Trader Joe /// @notice Required interface of LBRouter contract interface ILBRouter { /// @dev The liquidity parameters, such as: /// - tokenX: The address of token X /// - tokenY: The address of token Y /// - binStep: The bin step of the pair /// - amountX: The amount to send of token X /// - amountY: The amount to send of token Y /// - amountXMin: The min amount of token X added to liquidity /// - amountYMin: The min amount of token Y added to liquidity /// - activeIdDesired: The active id that user wants to add liquidity from /// - idSlippage: The number of id that are allowed to slip /// - deltaIds: The list of delta ids to add liquidity (`deltaId = activeId - desiredId`) /// - distributionX: The distribution of tokenX with sum(distributionX) = 100e18 (100%) or 0 (0%) /// - distributionY: The distribution of tokenY with sum(distributionY) = 100e18 (100%) or 0 (0%) /// - to: The address of the recipient /// - deadline: The deadline of the tx struct LiquidityParameters { IERC20 tokenX; IERC20 tokenY; uint256 binStep; uint256 amountX; uint256 amountY; uint256 amountXMin; uint256 amountYMin; uint256 activeIdDesired; uint256 idSlippage; int256[] deltaIds; uint256[] distributionX; uint256[] distributionY; address to; uint256 deadline; } function factory() external view returns (ILBFactory); function oldFactory() external view returns (IJoeFactory); function wavax() external view returns (IWAVAX); function getIdFromPrice(ILBPair LBPair, uint256 price) external view returns (uint24); function getPriceFromId(ILBPair LBPair, uint24 id) external view returns (uint256); function getSwapIn( ILBPair LBPair, uint256 amountOut, bool swapForY ) external view returns (uint256 amountIn, uint256 feesIn); function getSwapOut( ILBPair LBPair, uint256 amountIn, bool swapForY ) external view returns (uint256 amountOut, uint256 feesIn); function createLBPair( IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep ) external returns (ILBPair pair); function addLiquidity(LiquidityParameters calldata liquidityParameters) external returns (uint256[] memory depositIds, uint256[] memory liquidityMinted); function addLiquidityAVAX(LiquidityParameters calldata liquidityParameters) external payable returns (uint256[] memory depositIds, uint256[] memory liquidityMinted); function removeLiquidity( IERC20 tokenX, IERC20 tokenY, uint16 binStep, uint256 amountXMin, uint256 amountYMin, uint256[] memory ids, uint256[] memory amounts, address to, uint256 deadline ) external returns (uint256 amountX, uint256 amountY); function removeLiquidityAVAX( IERC20 token, uint16 binStep, uint256 amountTokenMin, uint256 amountAVAXMin, uint256[] memory ids, uint256[] memory amounts, address payable to, uint256 deadline ) external returns (uint256 amountToken, uint256 amountAVAX); function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, uint256[] memory pairBinSteps, IERC20[] memory tokenPath, address to, uint256 deadline ) external returns (uint256 amountOut); function swapExactTokensForAVAX( uint256 amountIn, uint256 amountOutMinAVAX, uint256[] memory pairBinSteps, IERC20[] memory tokenPath, address payable to, uint256 deadline ) external returns (uint256 amountOut); function swapExactAVAXForTokens( uint256 amountOutMin, uint256[] memory pairBinSteps, IERC20[] memory tokenPath, address to, uint256 deadline ) external payable returns (uint256 amountOut); function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, uint256[] memory pairBinSteps, IERC20[] memory tokenPath, address to, uint256 deadline ) external returns (uint256[] memory amountsIn); function swapTokensForExactAVAX( uint256 amountOut, uint256 amountInMax, uint256[] memory pairBinSteps, IERC20[] memory tokenPath, address payable to, uint256 deadline ) external returns (uint256[] memory amountsIn); function swapAVAXForExactTokens( uint256 amountOut, uint256[] memory pairBinSteps, IERC20[] memory tokenPath, address to, uint256 deadline ) external payable returns (uint256[] memory amountsIn); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, uint256[] memory pairBinSteps, IERC20[] memory tokenPath, address to, uint256 deadline ) external returns (uint256 amountOut); function swapExactTokensForAVAXSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMinAVAX, uint256[] memory pairBinSteps, IERC20[] memory tokenPath, address payable to, uint256 deadline ) external returns (uint256 amountOut); function swapExactAVAXForTokensSupportingFeeOnTransferTokens( uint256 amountOutMin, uint256[] memory pairBinSteps, IERC20[] memory tokenPath, address to, uint256 deadline ) external payable returns (uint256 amountOut); function sweep( IERC20 token, address to, uint256 amount ) external; function sweepLBToken( ILBToken _lbToken, address _to, uint256[] calldata _ids, uint256[] calldata _amounts ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /// @title Liquidity Book Token Interface /// @author Trader Joe /// @notice Required interface of LBToken contract interface ILBToken is IERC165 { event TransferSingle(address indexed sender, address indexed from, address indexed to, uint256 id, uint256 amount); event TransferBatch( address indexed sender, address indexed from, address indexed to, uint256[] ids, uint256[] amounts ); event ApprovalForAll(address indexed account, address indexed sender, bool approved); function name() external view returns (string memory); function symbol() external view returns (string memory); function balanceOf(address account, uint256 id) external view returns (uint256); function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory batchBalances); function totalSupply(uint256 id) external view returns (uint256); function isApprovedForAll(address owner, address spender) external view returns (bool); function setApprovalForAll(address sender, bool approved) external; function safeTransferFrom( address from, address to, uint256 id, uint256 amount ) external; function safeBatchTransferFrom( address from, address to, uint256[] calldata id, uint256[] calldata amount ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /// @title Liquidity Book Pending Ownable Interface /// @author Trader Joe /// @notice Required interface of Pending Ownable contract used for LBFactory interface IPendingOwnable { event PendingOwnerSet(address indexed pendingOwner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function owner() external view returns (address); function pendingOwner() external view returns (address); function setPendingOwner(address pendingOwner) external; function revokePendingOwner() external; function becomeOwner() external; function renounceOwnership() external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @title WAVAX Interface /// @notice Required interface of Wrapped AVAX contract interface IWAVAX is IERC20 { function deposit() external payable; function withdraw(uint256) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "../LBErrors.sol"; import "./Math128x128.sol"; /// @title Liquidity Book Bin Helper Library /// @author Trader Joe /// @notice Contract used to convert bin ID to price and back library BinHelper { using Math128x128 for uint256; int256 private constant REAL_ID_SHIFT = 1 << 23; /// @notice Returns the id corresponding to the given price /// @dev The id may be inaccurate due to rounding issues, always trust getPriceFromId rather than /// getIdFromPrice /// @param _price The price of y per x as a 128.128-binary fixed-point number /// @param _binStep The bin step /// @return The id corresponding to this price function getIdFromPrice(uint256 _price, uint256 _binStep) internal pure returns (uint24) { unchecked { uint256 _binStepValue = _getBPValue(_binStep); // can't overflow as `2**23 + log2(price) < 2**23 + 2**128 < max(uint256)` int256 _id = REAL_ID_SHIFT + _price.log2() / _binStepValue.log2(); if (_id < 0 || uint256(_id) > type(uint24).max) revert BinHelper__IdOverflows(); return uint24(uint256(_id)); } } /// @notice Returns the price corresponding to the given ID, as a 128.128-binary fixed-point number /// @dev This is the trusted function to link id to price, the other way may be inaccurate /// @param _id The id /// @param _binStep The bin step /// @return The price corresponding to this id, as a 128.128-binary fixed-point number function getPriceFromId(uint256 _id, uint256 _binStep) internal pure returns (uint256) { if (_id > uint256(type(uint24).max)) revert BinHelper__IdOverflows(); unchecked { int256 _realId = int256(_id) - REAL_ID_SHIFT; return _getBPValue(_binStep).power(_realId); } } /// @notice Returns the (1 + bp) value as a 128.128-decimal fixed-point number /// @param _binStep The bp value in [1; 100] (referring to 0.01% to 1%) /// @return The (1+bp) value as a 128.128-decimal fixed-point number function _getBPValue(uint256 _binStep) internal pure returns (uint256) { if (_binStep == 0 || _binStep > Constants.BASIS_POINT_MAX) revert BinHelper__BinStepOverflows(_binStep); unchecked { // can't overflow as `max(result) = 2**128 + 10_000 << 128 / 10_000 < max(uint256)` return Constants.SCALE + (_binStep << Constants.SCALE_OFFSET) / Constants.BASIS_POINT_MAX; } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /// @title Liquidity Book Bit Math Library /// @author Trader Joe /// @notice Helper contract used for bit calculations library BitMath { /// @notice Returns the closest non-zero bit of `integer` to the right (of left) of the `bit` bits that is not `bit` /// @param _integer The integer as a uint256 /// @param _bit The bit index /// @param _rightSide Whether we're searching in the right side of the tree (true) or the left side (false) /// @return The index of the closest non-zero bit. If there is no closest bit, it returns max(uint256) function closestBit( uint256 _integer, uint8 _bit, bool _rightSide ) internal pure returns (uint256) { return _rightSide ? closestBitRight(_integer, _bit - 1) : closestBitLeft(_integer, _bit + 1); } /// @notice Returns the most (or least) significant bit of `_integer` /// @param _integer The integer /// @param _isMostSignificant Whether we want the most (true) or the least (false) significant bit /// @return The index of the most (or least) significant bit function significantBit(uint256 _integer, bool _isMostSignificant) internal pure returns (uint8) { return _isMostSignificant ? mostSignificantBit(_integer) : leastSignificantBit(_integer); } /// @notice Returns the index of the closest bit on the right of x that is non null /// @param x The value as a uint256 /// @param bit The index of the bit to start searching at /// @return id The index of the closest non null bit on the right of x. /// If there is no closest bit, it returns max(uint256) function closestBitRight(uint256 x, uint8 bit) internal pure returns (uint256 id) { unchecked { uint256 _shift = 255 - bit; x <<= _shift; // can't overflow as it's non-zero and we shifted it by `_shift` return (x == 0) ? type(uint256).max : mostSignificantBit(x) - _shift; } } /// @notice Returns the index of the closest bit on the left of x that is non null /// @param x The value as a uint256 /// @param bit The index of the bit to start searching at /// @return id The index of the closest non null bit on the left of x. /// If there is no closest bit, it returns max(uint256) function closestBitLeft(uint256 x, uint8 bit) internal pure returns (uint256 id) { unchecked { x >>= bit; return (x == 0) ? type(uint256).max : leastSignificantBit(x) + bit; } } /// @notice Returns the index of the most significant bit of x /// @param x The value as a uint256 /// @return msb The index of the most significant bit of x function mostSignificantBit(uint256 x) internal pure returns (uint8 msb) { unchecked { if (x >= 1 << 128) { x >>= 128; msb = 128; } if (x >= 1 << 64) { x >>= 64; msb += 64; } if (x >= 1 << 32) { x >>= 32; msb += 32; } if (x >= 1 << 16) { x >>= 16; msb += 16; } if (x >= 1 << 8) { x >>= 8; msb += 8; } if (x >= 1 << 4) { x >>= 4; msb += 4; } if (x >= 1 << 2) { x >>= 2; msb += 2; } if (x >= 1 << 1) { msb += 1; } } } /// @notice Returns the index of the least significant bit of x /// @param x The value as a uint256 /// @return lsb The index of the least significant bit of x function leastSignificantBit(uint256 x) internal pure returns (uint8 lsb) { unchecked { if (x << 128 != 0) { x <<= 128; lsb = 128; } if (x << 64 != 0) { x <<= 64; lsb += 64; } if (x << 32 != 0) { x <<= 32; lsb += 32; } if (x << 16 != 0) { x <<= 16; lsb += 16; } if (x << 8 != 0) { x <<= 8; lsb += 8; } if (x << 4 != 0) { x <<= 4; lsb += 4; } if (x << 2 != 0) { x <<= 2; lsb += 2; } if (x << 1 != 0) { lsb += 1; } return 255 - lsb; } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /// @title Liquidity Book Constants Library /// @author Trader Joe /// @notice Set of constants for Liquidity Book contracts library Constants { uint256 internal constant SCALE_OFFSET = 128; uint256 internal constant SCALE = 1 << SCALE_OFFSET; uint256 internal constant PRECISION = 1e18; uint256 internal constant BASIS_POINT_MAX = 10_000; /// @dev The expected return after a successful flash loan bytes32 internal constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./Constants.sol"; import "./SafeCast.sol"; import "./SafeMath.sol"; /// @title Liquidity Book Fee Helper Library /// @author Trader Joe /// @notice Helper contract used for fees calculation library FeeHelper { using SafeCast for uint256; using SafeMath for uint256; /// @dev Structure to store the protocol fees: /// - binStep: The bin step /// - baseFactor: The base factor /// - filterPeriod: The filter period, where the fees stays constant /// - decayPeriod: The decay period, where the fees are halved /// - reductionFactor: The reduction factor, used to calculate the reduction of the accumulator /// - variableFeeControl: The variable fee control, used to control the variable fee, can be 0 to disable them /// - protocolShare: The share of fees sent to protocol /// - maxVolatilityAccumulated: The max value of volatility accumulated /// - volatilityAccumulated: The value of volatility accumulated /// - volatilityReference: The value of volatility reference /// - indexRef: The index reference /// - time: The last time the accumulator was called struct FeeParameters { // 144 lowest bits in slot uint16 binStep; uint16 baseFactor; uint16 filterPeriod; uint16 decayPeriod; uint16 reductionFactor; uint24 variableFeeControl; uint16 protocolShare; uint24 maxVolatilityAccumulated; // 112 highest bits in slot uint24 volatilityAccumulated; uint24 volatilityReference; uint24 indexRef; uint40 time; } /// @dev Structure used during swaps to distributes the fees: /// - total: The total amount of fees /// - protocol: The amount of fees reserved for protocol struct FeesDistribution { uint128 total; uint128 protocol; } /// @notice Update the value of the volatility accumulated /// @param _fp The current fee parameters /// @param _activeId The current active id function updateVariableFeeParameters(FeeParameters memory _fp, uint256 _activeId) internal view { uint256 _deltaT = block.timestamp - _fp.time; if (_deltaT >= _fp.filterPeriod || _fp.time == 0) { _fp.indexRef = uint24(_activeId); if (_deltaT < _fp.decayPeriod) { unchecked { // This can't overflow as `reductionFactor <= BASIS_POINT_MAX` _fp.volatilityReference = uint24( (uint256(_fp.reductionFactor) * _fp.volatilityAccumulated) / Constants.BASIS_POINT_MAX ); } } else { _fp.volatilityReference = 0; } } _fp.time = (block.timestamp).safe40(); updateVolatilityAccumulated(_fp, _activeId); } /// @notice Update the volatility accumulated /// @param _fp The fee parameter /// @param _activeId The current active id function updateVolatilityAccumulated(FeeParameters memory _fp, uint256 _activeId) internal pure { uint256 volatilityAccumulated = (_activeId.absSub(_fp.indexRef) * Constants.BASIS_POINT_MAX) + _fp.volatilityReference; _fp.volatilityAccumulated = volatilityAccumulated > _fp.maxVolatilityAccumulated ? _fp.maxVolatilityAccumulated : uint24(volatilityAccumulated); } /// @notice Returns the base fee added to a swap, with 18 decimals /// @param _fp The current fee parameters /// @return The fee with 18 decimals precision function getBaseFee(FeeParameters memory _fp) internal pure returns (uint256) { unchecked { return uint256(_fp.baseFactor) * _fp.binStep * 1e10; } } /// @notice Returns the variable fee added to a swap, with 18 decimals /// @param _fp The current fee parameters /// @return variableFee The variable fee with 18 decimals precision function getVariableFee(FeeParameters memory _fp) internal pure returns (uint256 variableFee) { if (_fp.variableFeeControl != 0) { // Can't overflow as the max value is `max(uint24) * (max(uint24) * max(uint16)) ** 2 < max(uint104)` // It returns 18 decimals as: // decimals(variableFeeControl * (volatilityAccumulated * binStep)**2 / 100) = 4 + (4 + 4) * 2 - 2 = 18 unchecked { uint256 _prod = uint256(_fp.volatilityAccumulated) * _fp.binStep; variableFee = (_prod * _prod * _fp.variableFeeControl + 99) / 100; } } } /// @notice Return the amount of fees from an amount /// @dev Rounds amount up, follows `amount = amountWithFees - getFeeAmountFrom(fp, amountWithFees)` /// @param _fp The current fee parameter /// @param _amountWithFees The amount of token sent /// @return The fee amount from the amount sent function getFeeAmountFrom(FeeParameters memory _fp, uint256 _amountWithFees) internal pure returns (uint256) { return (_amountWithFees * getTotalFee(_fp) + Constants.PRECISION - 1) / (Constants.PRECISION); } /// @notice Return the fees to add to an amount /// @dev Rounds amount up, follows `amountWithFees = amount + getFeeAmount(fp, amount)` /// @param _fp The current fee parameter /// @param _amount The amount of token sent /// @return The fee amount to add to the amount function getFeeAmount(FeeParameters memory _fp, uint256 _amount) internal pure returns (uint256) { uint256 _fee = getTotalFee(_fp); uint256 _denominator = Constants.PRECISION - _fee; return (_amount * _fee + _denominator - 1) / _denominator; } /// @notice Return the fees added when an user adds liquidity and change the ratio in the active bin /// @dev Rounds amount up /// @param _fp The current fee parameter /// @param _amountWithFees The amount of token sent /// @return The fee amount function getFeeAmountForC(FeeParameters memory _fp, uint256 _amountWithFees) internal pure returns (uint256) { uint256 _fee = getTotalFee(_fp); uint256 _denominator = Constants.PRECISION * Constants.PRECISION; return (_amountWithFees * _fee * (_fee + Constants.PRECISION) + _denominator - 1) / _denominator; } /// @notice Return the fees distribution added to an amount /// @param _fp The current fee parameter /// @param _fees The fee amount /// @return fees The fee distribution function getFeeAmountDistribution(FeeParameters memory _fp, uint256 _fees) internal pure returns (FeesDistribution memory fees) { fees.total = _fees.safe128(); // unsafe math is fine because total >= protocol unchecked { fees.protocol = uint128((_fees * _fp.protocolShare) / Constants.BASIS_POINT_MAX); } } /// @notice Return the total fee, i.e. baseFee + variableFee /// @param _fp The current fee parameter /// @return The total fee, with 18 decimals function getTotalFee(FeeParameters memory _fp) private pure returns (uint256) { unchecked { return getBaseFee(_fp) + getVariableFee(_fp); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "../LBErrors.sol"; import "./BitMath.sol"; import "./Constants.sol"; import "./Math512Bits.sol"; /// @title Liquidity Book Math Helper Library /// @author Trader Joe /// @notice Helper contract used for power and log calculations library Math128x128 { using Math512Bits for uint256; using BitMath for uint256; uint256 constant LOG_SCALE_OFFSET = 127; uint256 constant LOG_SCALE = 1 << LOG_SCALE_OFFSET; uint256 constant LOG_SCALE_SQUARED = LOG_SCALE * LOG_SCALE; /// @notice Calculates the binary logarithm of x. /// /// @dev Based on the iterative approximation algorithm. /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation /// /// Requirements: /// - x must be greater than zero. /// /// Caveats: /// - The results are not perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation /// Also because x is converted to an unsigned 129.127-binary fixed-point number during the operation to optimize the multiplication /// /// @param x The unsigned 128.128-binary fixed-point number for which to calculate the binary logarithm. /// @return result The binary logarithm as a signed 128.128-binary fixed-point number. function log2(uint256 x) internal pure returns (int256 result) { // Convert x to a unsigned 129.127-binary fixed-point number to optimize the multiplication. // If we use an offset of 128 bits, y would need 129 bits and y**2 would would overflow and we would have to // use mulDiv, by reducing x to 129.127-binary fixed-point number we assert that y will use 128 bits, and we // can use the regular multiplication if (x == 1) return -128; if (x == 0) revert Math128x128__LogUnderflow(); x >>= 1; unchecked { // This works because log2(x) = -log2(1/x). int256 sign; if (x >= LOG_SCALE) { sign = 1; } else { sign = -1; // Do the fixed-point inversion inline to save gas x = LOG_SCALE_SQUARED / x; } // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n). uint256 n = (x >> LOG_SCALE_OFFSET).mostSignificantBit(); // The integer part of the logarithm as a signed 129.127-binary fixed-point number. The operation can't overflow // because n is maximum 255, LOG_SCALE_OFFSET is 127 bits and sign is either 1 or -1. result = int256(n) << LOG_SCALE_OFFSET; // This is y = x * 2^(-n). uint256 y = x >> n; // If y = 1, the fractional part is zero. if (y != LOG_SCALE) { // Calculate the fractional part via the iterative approximation. // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster. for (int256 delta = int256(1 << (LOG_SCALE_OFFSET - 1)); delta > 0; delta >>= 1) { y = (y * y) >> LOG_SCALE_OFFSET; // Is y^2 > 2 and so in the range [2,4)? if (y >= 1 << (LOG_SCALE_OFFSET + 1)) { // Add the 2^(-m) factor to the logarithm. result += delta; // Corresponds to z/2 on Wikipedia. y >>= 1; } } } // Convert x back to unsigned 128.128-binary fixed-point number result = (result * sign) << 1; } } /// @notice Returns the value of x^y. It calculates `1 / x^abs(y)` if x is bigger than 2^128. /// At the end of the operations, we invert the result if needed. /// @param x The unsigned 128.128-binary fixed-point number for which to calculate the power /// @param y A relative number without any decimals, needs to be between ]2^20; 2^20[ /// @return result The result of `x^y` function power(uint256 x, int256 y) internal pure returns (uint256 result) { bool invert; uint256 absY; if (y == 0) return Constants.SCALE; assembly { absY := y if slt(absY, 0) { absY := sub(0, absY) invert := iszero(invert) } } if (absY < 0x100000) { result = Constants.SCALE; assembly { let pow := x if gt(x, 0xffffffffffffffffffffffffffffffff) { pow := div(not(0), pow) invert := iszero(invert) } if and(absY, 0x1) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x2) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x4) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x8) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x10) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x20) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x40) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x80) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x100) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x200) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x400) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x800) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x1000) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x2000) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x4000) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x8000) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x10000) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x20000) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x40000) { result := shr(128, mul(result, pow)) } pow := shr(128, mul(pow, pow)) if and(absY, 0x80000) { result := shr(128, mul(result, pow)) } } } // revert if y is too big or if x^y underflowed if (result == 0) revert Math128x128__PowerUnderflow(x, y); return invert ? type(uint256).max / result : result; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "../LBErrors.sol"; import "./BitMath.sol"; /// @title Liquidity Book Math Helper Library /// @author Trader Joe /// @notice Helper contract used for full precision calculations library Math512Bits { using BitMath for uint256; /// @notice Calculates floor(x*y÷denominator) with full precision /// The result will be rounded down /// /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv /// /// Requirements: /// - The denominator cannot be zero /// - The result must fit within uint256 /// /// Caveats: /// - This function does not work with fixed-point numbers /// /// @param x The multiplicand as an uint256 /// @param y The multiplier as an uint256 /// @param denominator The divisor as an uint256 /// @return result The result as an uint256 function mulDivRoundDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { (uint256 prod0, uint256 prod1) = _getMulProds(x, y); return _getEndOfDivRoundDown(x, y, denominator, prod0, prod1); } /// @notice Calculates x * y >> offset with full precision /// The result will be rounded down /// /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv /// /// Requirements: /// - The offset needs to be strictly lower than 256 /// - The result must fit within uint256 /// /// Caveats: /// - This function does not work with fixed-point numbers /// /// @param x The multiplicand as an uint256 /// @param y The multiplier as an uint256 /// @param offset The offset as an uint256, can't be greater than 256 /// @return result The result as an uint256 function mulShiftRoundDown( uint256 x, uint256 y, uint256 offset ) internal pure returns (uint256 result) { if (offset > 255) revert Math512Bits__OffsetOverflows(offset); (uint256 prod0, uint256 prod1) = _getMulProds(x, y); if (prod0 != 0) result = prod0 >> offset; if (prod1 != 0) { // Make sure the result is less than 2^256. if (prod1 >= 1 << offset) revert Math512Bits__MulShiftOverflow(prod1, offset); unchecked { result += prod1 << (256 - offset); } } } /// @notice Calculates x * y >> offset with full precision /// The result will be rounded up /// /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv /// /// Requirements: /// - The offset needs to be strictly lower than 256 /// - The result must fit within uint256 /// /// Caveats: /// - This function does not work with fixed-point numbers /// /// @param x The multiplicand as an uint256 /// @param y The multiplier as an uint256 /// @param offset The offset as an uint256, can't be greater than 256 /// @return result The result as an uint256 function mulShiftRoundUp( uint256 x, uint256 y, uint256 offset ) internal pure returns (uint256 result) { unchecked { result = mulShiftRoundDown(x, y, offset); if (mulmod(x, y, 1 << offset) != 0) result += 1; } } /// @notice Calculates x << offset / y with full precision /// The result will be rounded down /// /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv /// /// Requirements: /// - The offset needs to be strictly lower than 256 /// - The result must fit within uint256 /// /// Caveats: /// - This function does not work with fixed-point numbers /// /// @param x The multiplicand as an uint256 /// @param offset The number of bit to shift x as an uint256 /// @param denominator The divisor as an uint256 /// @return result The result as an uint256 function shiftDivRoundDown( uint256 x, uint256 offset, uint256 denominator ) internal pure returns (uint256 result) { if (offset > 255) revert Math512Bits__OffsetOverflows(offset); uint256 prod0; uint256 prod1; prod0 = x << offset; // Least significant 256 bits of the product unchecked { prod1 = x >> (256 - offset); // Most significant 256 bits of the product } return _getEndOfDivRoundDown(x, 1 << offset, denominator, prod0, prod1); } /// @notice Calculates x << offset / y with full precision /// The result will be rounded up /// /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv /// /// Requirements: /// - The offset needs to be strictly lower than 256 /// - The result must fit within uint256 /// /// Caveats: /// - This function does not work with fixed-point numbers /// /// @param x The multiplicand as an uint256 /// @param offset The number of bit to shift x as an uint256 /// @param denominator The divisor as an uint256 /// @return result The result as an uint256 function shiftDivRoundUp( uint256 x, uint256 offset, uint256 denominator ) internal pure returns (uint256 result) { result = shiftDivRoundDown(x, offset, denominator); unchecked { if (mulmod(x, 1 << offset, denominator) != 0) result += 1; } } /// @notice Helper function to return the result of `x * y` as 2 uint256 /// @param x The multiplicand as an uint256 /// @param y The multiplier as an uint256 /// @return prod0 The least significant 256 bits of the product /// @return prod1 The most significant 256 bits of the product function _getMulProds(uint256 x, uint256 y) private pure returns (uint256 prod0, uint256 prod1) { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } } /// @notice Helper function to return the result of `x * y / denominator` with full precision /// @param x The multiplicand as an uint256 /// @param y The multiplier as an uint256 /// @param denominator The divisor as an uint256 /// @param prod0 The least significant 256 bits of the product /// @param prod1 The most significant 256 bits of the product /// @return result The result as an uint256 function _getEndOfDivRoundDown( uint256 x, uint256 y, uint256 denominator, uint256 prod0, uint256 prod1 ) private pure returns (uint256 result) { // Handle non-overflow cases, 256 by 256 division if (prod1 == 0) { unchecked { result = prod0 / denominator; } } else { // Make sure the result is less than 2^256. Also prevents denominator == 0 if (prod1 >= denominator) revert Math512Bits__MulDivOverflow(prod1, denominator); // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1 // See https://cs.stackexchange.com/q/138556/92363 unchecked { // Does not overflow because the denominator cannot be zero at this stage in the function uint256 lpotdod = denominator & (~denominator + 1); assembly { // Divide denominator by lpotdod. denominator := div(denominator, lpotdod) // Divide [prod1 prod0] by lpotdod. prod0 := div(prod0, lpotdod) // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one lpotdod := add(div(sub(0, lpotdod), lpotdod), 1) } // Shift in bits from prod1 into prod0 prod0 |= prod1 * lpotdod; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4 uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "../LBErrors.sol"; /// @title Liquidity Book Safe Cast Library /// @author Trader Joe /// @notice Helper contract used for converting uint values safely library SafeCast { /// @notice Returns x on uint248 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint248 function safe248(uint256 x) internal pure returns (uint248 y) { if ((y = uint248(x)) != x) revert SafeCast__Exceeds248Bits(x); } /// @notice Returns x on uint240 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint240 function safe240(uint256 x) internal pure returns (uint240 y) { if ((y = uint240(x)) != x) revert SafeCast__Exceeds240Bits(x); } /// @notice Returns x on uint232 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint232 function safe232(uint256 x) internal pure returns (uint232 y) { if ((y = uint232(x)) != x) revert SafeCast__Exceeds232Bits(x); } /// @notice Returns x on uint224 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint224 function safe224(uint256 x) internal pure returns (uint224 y) { if ((y = uint224(x)) != x) revert SafeCast__Exceeds224Bits(x); } /// @notice Returns x on uint216 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint216 function safe216(uint256 x) internal pure returns (uint216 y) { if ((y = uint216(x)) != x) revert SafeCast__Exceeds216Bits(x); } /// @notice Returns x on uint208 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint208 function safe208(uint256 x) internal pure returns (uint208 y) { if ((y = uint208(x)) != x) revert SafeCast__Exceeds208Bits(x); } /// @notice Returns x on uint200 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint200 function safe200(uint256 x) internal pure returns (uint200 y) { if ((y = uint200(x)) != x) revert SafeCast__Exceeds200Bits(x); } /// @notice Returns x on uint192 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint192 function safe192(uint256 x) internal pure returns (uint192 y) { if ((y = uint192(x)) != x) revert SafeCast__Exceeds192Bits(x); } /// @notice Returns x on uint184 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint184 function safe184(uint256 x) internal pure returns (uint184 y) { if ((y = uint184(x)) != x) revert SafeCast__Exceeds184Bits(x); } /// @notice Returns x on uint176 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint176 function safe176(uint256 x) internal pure returns (uint176 y) { if ((y = uint176(x)) != x) revert SafeCast__Exceeds176Bits(x); } /// @notice Returns x on uint168 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint168 function safe168(uint256 x) internal pure returns (uint168 y) { if ((y = uint168(x)) != x) revert SafeCast__Exceeds168Bits(x); } /// @notice Returns x on uint160 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint160 function safe160(uint256 x) internal pure returns (uint160 y) { if ((y = uint160(x)) != x) revert SafeCast__Exceeds160Bits(x); } /// @notice Returns x on uint152 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint152 function safe152(uint256 x) internal pure returns (uint152 y) { if ((y = uint152(x)) != x) revert SafeCast__Exceeds152Bits(x); } /// @notice Returns x on uint144 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint144 function safe144(uint256 x) internal pure returns (uint144 y) { if ((y = uint144(x)) != x) revert SafeCast__Exceeds144Bits(x); } /// @notice Returns x on uint136 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint136 function safe136(uint256 x) internal pure returns (uint136 y) { if ((y = uint136(x)) != x) revert SafeCast__Exceeds136Bits(x); } /// @notice Returns x on uint128 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint128 function safe128(uint256 x) internal pure returns (uint128 y) { if ((y = uint128(x)) != x) revert SafeCast__Exceeds128Bits(x); } /// @notice Returns x on uint120 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint120 function safe120(uint256 x) internal pure returns (uint120 y) { if ((y = uint120(x)) != x) revert SafeCast__Exceeds120Bits(x); } /// @notice Returns x on uint112 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint112 function safe112(uint256 x) internal pure returns (uint112 y) { if ((y = uint112(x)) != x) revert SafeCast__Exceeds112Bits(x); } /// @notice Returns x on uint104 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint104 function safe104(uint256 x) internal pure returns (uint104 y) { if ((y = uint104(x)) != x) revert SafeCast__Exceeds104Bits(x); } /// @notice Returns x on uint96 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint96 function safe96(uint256 x) internal pure returns (uint96 y) { if ((y = uint96(x)) != x) revert SafeCast__Exceeds96Bits(x); } /// @notice Returns x on uint88 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint88 function safe88(uint256 x) internal pure returns (uint88 y) { if ((y = uint88(x)) != x) revert SafeCast__Exceeds88Bits(x); } /// @notice Returns x on uint80 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint80 function safe80(uint256 x) internal pure returns (uint80 y) { if ((y = uint80(x)) != x) revert SafeCast__Exceeds80Bits(x); } /// @notice Returns x on uint72 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint72 function safe72(uint256 x) internal pure returns (uint72 y) { if ((y = uint72(x)) != x) revert SafeCast__Exceeds72Bits(x); } /// @notice Returns x on uint64 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint64 function safe64(uint256 x) internal pure returns (uint64 y) { if ((y = uint64(x)) != x) revert SafeCast__Exceeds64Bits(x); } /// @notice Returns x on uint56 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint56 function safe56(uint256 x) internal pure returns (uint56 y) { if ((y = uint56(x)) != x) revert SafeCast__Exceeds56Bits(x); } /// @notice Returns x on uint48 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint48 function safe48(uint256 x) internal pure returns (uint48 y) { if ((y = uint48(x)) != x) revert SafeCast__Exceeds48Bits(x); } /// @notice Returns x on uint40 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint40 function safe40(uint256 x) internal pure returns (uint40 y) { if ((y = uint40(x)) != x) revert SafeCast__Exceeds40Bits(x); } /// @notice Returns x on uint32 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint32 function safe32(uint256 x) internal pure returns (uint32 y) { if ((y = uint32(x)) != x) revert SafeCast__Exceeds32Bits(x); } /// @notice Returns x on uint24 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint24 function safe24(uint256 x) internal pure returns (uint24 y) { if ((y = uint24(x)) != x) revert SafeCast__Exceeds24Bits(x); } /// @notice Returns x on uint16 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint16 function safe16(uint256 x) internal pure returns (uint16 y) { if ((y = uint16(x)) != x) revert SafeCast__Exceeds16Bits(x); } /// @notice Returns x on uint8 and check that it does not overflow /// @param x The value as an uint256 /// @return y The value as an uint8 function safe8(uint256 x) internal pure returns (uint8 y) { if ((y = uint8(x)) != x) revert SafeCast__Exceeds8Bits(x); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /// @title Liquidity Book Safe Math Helper Library /// @author Trader Joe /// @notice Helper contract used for calculating absolute value safely library SafeMath { /// @notice absSub, can't underflow or overflow /// @param x The first value /// @param y The second value /// @return The result of abs(x - y) function absSub(uint256 x, uint256 y) internal pure returns (uint256) { unchecked { return x > y ? x - y : y - x; } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/security/Pausable.sol'; contract StratManager is Ownable, Pausable { /// @dev Contracts: /// {keeper} - Address to manage a few lower risk features of the strat incl rebalancing /// {vault} - Address of the vault that controls the strategy's funds. /// {joeRouter} - Address of exchange to execute swaps. /// {yakRouter} - Address of yakswap to optimally execute swaps. address public keeper; address public joeRouter; address public immutable vault; /// ----------------------------------------------------------- /// Events /// ----------------------------------------------------------- event SetKeeper(address keeper); event SetJoeRouter(address joeRouter); /** * @dev Initializes the base strategy. * @param _keeper address to use as alternative owner. * @param _joeRouter router to use for swaps. * @param _vault address of parent vault. */ constructor(address _keeper, address _joeRouter, address _vault) { keeper = _keeper; joeRouter = _joeRouter; vault = _vault; } // checks that caller is either owner or keeper. modifier onlyManager() { require(msg.sender == owner() || msg.sender == keeper, '!manager'); _; } /// @notice Updates address of the strat keeper. /// @param _keeper new keeper address. function setKeeper(address _keeper) external onlyManager { require(_keeper != address(0), 'StratManager: 0 address'); keeper = _keeper; emit SetKeeper(_keeper); } /// @notice Updates router that will be used for swaps. /// @param _joeRouter new joeRouter address. function setJoeRouter(address _joeRouter) external onlyOwner { require(_joeRouter != address(0), 'StratManager: 0 address'); joeRouter = _joeRouter; emit SetJoeRouter(_joeRouter); } /// @notice Function to synchronize balances before new user deposit. /// Can be overridden in the strategy. function beforeDeposit() external virtual {} }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; interface ILBStrategy { /// @notice The liquidity parameters, such as: /// @param tokenX: The address of token X /// @param tokenY: The address of token Y /// @param pair: The address of the LB pair /// @param binStep: BinStep as per LB Pair /// @param deltaIds: the bins you want to add liquidity to. Each value is relative to the active bin ID /// @param distributionX: The percentage of X you want to add to each bin in deltaIds /// @param distributionY: The percentage of Y you want to add to each bin in deltaIds /// @param idSlippage: The slippage tolerance in case active bin moves during time it takes to transact struct StrategyParameters { IERC20 tokenX; IERC20 tokenY; address pair; uint16 binStep; int256[] deltaIds; uint256[] distributionX; uint256[] distributionY; uint256 idSlippage; } function deltaIds() external view returns (int256[] memory deltaIds); function distributionX() external view returns (uint256[] memory distributionX); function distributionY() external view returns (uint256[] memory distributionY); function idSlippage() external view returns (uint256); function vault() external view returns (address); function lbPair() external view returns (address); function setKeeper(address _keeper) external; function keeper() external view returns (address); function strategyPositionAtIndex( uint256 _index ) external view returns (uint256); function strategyPositionNumber() external view returns (uint256); function checkProposedBinLength( int256[] memory proposedDeltas, uint256 activeId ) external view returns (uint256); function addLiquidity( uint256 amountX, uint256 amountY, uint256 amountXMin, uint256 amountYMin ) external returns (uint256[] memory liquidityMinted); function binStep() external view returns (uint16); function balanceOfLiquidities() external view returns (uint256 totalLiquidityBalance); function removeLiquidity( uint256 denominator ) external returns (uint256 amountX, uint256 amountY); function tokenX() external view returns (IERC20); function tokenY() external view returns (IERC20); function harvest() external returns (uint256 amountXReceived, uint256 amountYReceived); function earn() external; function retireStrat() external; function panic() external; function pause() external; function unpause() external; function paused() external view returns (bool); function joeRouter() external view returns (address); function binHasYLiquidity( int256[] memory _deltaIds ) external view returns (bool hasYLiquidity); function binHasXLiquidity( int256[] memory _deltaIds ) external view returns (bool hasXLiquidity); function beforeDeposit() external; function rewardsAvailable() external view returns (uint256 rewardsX, uint256 rewardsY); function executeRebalance( int256[] memory _deltaIds, uint256[] memory _distributionX, uint256[] memory _distributionY, uint256 _idSlippage ) external returns (uint256 liquidityAfter); function executeRebalance() external returns (uint256 amountX, uint256 amountY); function checkLengthsPerformRebalance() external; function strategyActiveBins() external view returns (uint256[] memory activeBins); function getBalanceX() external view returns (uint256); function getBalanceY() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.9.0; interface IYakRouter { struct FormattedOffer { uint256[] amounts; address[] adapters; address[] path; uint256 gasEstimate; } struct Trade { uint256 amountIn; uint256 amountOut; address[] path; address[] adapters; } function findBestPath( uint256 _amountIn, address _tokenIn, address _tokenOut, uint256 _maxSteps ) external view returns (FormattedOffer memory); function swapNoSplit( Trade calldata _trade, address _to, uint256 _fee ) external; function swapNoSplitFromAVAX( Trade calldata _trade, address _to, uint256 _fee ) external payable; function swapNoSplitToAVAX( Trade calldata _trade, address _to, uint256 _fee ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; import '../common/StratManager.sol'; import '../interfaces/ILBStrategy.sol'; import '../interfaces/IYakRouter.sol'; import '../LB/interfaces/ILBToken.sol'; import '../LB/interfaces/ILBPair.sol'; import '../LB/interfaces/ILBRouter.sol'; import '../LB-periph/LiquidityAmounts.sol'; /// @notice used in conjunction with a vault to manage TraderJoe Liquidity Book Positions contract StrategyTJLiquidityBookLB is StratManager { using SafeERC20 for IERC20; using EnumerableSet for EnumerableSet.UintSet; uint256 constant PRECISION = 1e18; uint256 public lastHarvest; /// @notice parameters which should not be changed after init IERC20 public immutable tokenX; IERC20 public immutable tokenY; ILBPair public immutable lbPair; ILBToken public immutable lbToken; uint16 public immutable binStep; /// @notice parameters which may be changed by a strategist int256[] public deltaIds; uint256[] public distributionX; uint256[] public distributionY; uint256 public idSlippage; /// @notice where strategy currently has a non-zero balance in bins EnumerableSet.UintSet private _activeBins; /// ----------------------------------------------------------- /// Events /// ----------------------------------------------------------- event CollectRewards( uint256 lastHarvest, uint256 amountXBefore, uint256 amountYBefore, uint256 amountXAfter, uint256 amountYAfter ); event FeeTransfer(uint256 amountX, uint256 amountY); event AddLiquidity( address user, uint256 amountX, uint256 amountY, uint256 liquidity ); event RemoveLiquidity(address user, uint256 amountX, uint256 amountY); event Rebalance( uint256 amountXBefore, uint256 amountYBefore, uint256 amountXAfter, uint256 amountYAfter ); /// ----------------------------------------------------------- /// Constructor /// ----------------------------------------------------------- constructor( address _joeRouter, address _keeper, address _vault, ILBStrategy.StrategyParameters memory _strategyParams ) StratManager(_keeper, _joeRouter, _vault) { //require strategy to use < 50 bins due to gas limits require( _strategyParams.deltaIds.length < 50, 'Strategy: Too many bins' ); //check parameters are equal in length require( _strategyParams.deltaIds.length == _strategyParams.distributionX.length && _strategyParams.distributionX.length == _strategyParams.distributionY.length, 'Strategy: Unbalanced params' ); //check distributions are equal if (binHasXLiquidity(_strategyParams.deltaIds)) { _checkDistribution(_strategyParams.distributionX); } if (binHasYLiquidity(_strategyParams.deltaIds)) { _checkDistribution(_strategyParams.distributionY); } //set immutable strategy parameters tokenX = _strategyParams.tokenX; tokenY = _strategyParams.tokenY; lbPair = ILBPair(_strategyParams.pair); lbToken = ILBToken(_strategyParams.pair); binStep = _strategyParams.binStep; //set strategist controlled initial parameters deltaIds = _strategyParams.deltaIds; idSlippage = _strategyParams.idSlippage; distributionX = _strategyParams.distributionX; distributionY = _strategyParams.distributionY; //give the strategy the required allowances _giveAllowances(); } /// ----------------------------------------------------------- /// Track Strategy Bin Functions /// ----------------------------------------------------------- /// @notice Returns the type id at index `_index` where strategy has a non-zero balance /// @param _index The position index /// @return The non-zero position at index `_index` function strategyPositionAtIndex( uint256 _index ) public view returns (uint256) { return _activeBins.at(_index); } /// @notice Returns the number of non-zero balances of strategy /// @return The number of non-zero balances of strategy function strategyPositionNumber() public view returns (uint256) { return _activeBins.length(); } /// @notice returns all of the active bin Ids in the strategy /// @return activeBins currently in use by the strategy function strategyActiveBins() public view returns (uint256[] memory activeBins) { activeBins = new uint256[](_activeBins.length()); for (uint256 i; i < _activeBins.length(); i++) { activeBins[i] = strategyPositionAtIndex(i); } } /// @notice checks the proposed bin length /// @notice helps to ensure that we wont exceed the 50 bin limit in single call /// @return numProposedIds number of proposed bin Ids function checkProposedBinLength( int256[] memory proposedDeltas, uint256 activeId ) public view returns (uint256) { uint256 newIdCount; for (uint256 i; i < proposedDeltas.length; i++) { int256 _id = int256(activeId) + proposedDeltas[i]; //if the proposed ID doesnt exist count it if (!_activeBins.contains(uint256(_id))) { newIdCount += 1; } } return (newIdCount + strategyPositionNumber()); } /// ----------------------------------------------------------- /// Internal functions /// ----------------------------------------------------------- /// Internal function that adds liquidity to AMM and gets Liquidity Book tokens. /// @param amountX max amount of token X to add as liquidity /// @param amountY max amount of token Y to add as liquidity /// @param amountXMin min amount of token X to add as liquidity /// @param amountYMin min amount of token Y to add as liquidity /// @return depositIds the depositIds where liquidity was minted /// @return liquidityMinted set of liquidity deposited in each bin function _addLiquidity( uint256 amountX, uint256 amountY, uint256 amountXMin, uint256 amountYMin ) internal whenNotPaused returns (uint256[] memory depositIds, uint256[] memory liquidityMinted) { //fetch the current active Id. (, , uint256 activeId) = lbPair.getReservesAndId(); //check bin lengths are less than 50 bins due to block limit //further liquidity cannot be added until rebalanced require( checkProposedBinLength(deltaIds, activeId) < 50, 'Strategy: Requires Rebalance' ); //set the required liquidity parameters ILBRouter.LiquidityParameters memory liquidityParameters = ILBRouter .LiquidityParameters( tokenX, tokenY, binStep, amountX, amountY, amountXMin, amountYMin, activeId, idSlippage, deltaIds, distributionX, distributionY, address(this), block.timestamp ); //add the required liquidity into the pair (depositIds, liquidityMinted) = ILBRouter(joeRouter).addLiquidity( liquidityParameters ); //sum the total amount of liquidity minted uint256 liquidtyMintedHolder; for (uint256 i; i < liquidityMinted.length; i++) { liquidtyMintedHolder += liquidityMinted[i]; //add the ID's to the user set _activeBins.add(depositIds[i]); } require( _activeBins.length() < 50, 'Strategy: Too many bins after add; manager check bin slippage and call rebalance' ); //emit an event emit AddLiquidity(msg.sender, amountX, amountY, liquidtyMintedHolder); } /// @notice performs the removal of liquidity from the pair and keeps tokens in strategy /// @param denominator the ownership stake to remove /// @notice PRECISION is full amount i.e all liquidity to be removed /// @return amountX the amount of liquidity removed as tokenX /// @return amountY the amount of liquidity removed as tokenY function _removeLiquidity( uint256 denominator ) internal returns (uint256 amountX, uint256 amountY) { uint256[] memory activeBinIds = getActiveBinIds(); uint256[] memory amounts = new uint256[](activeBinIds.length); uint256 totalXBalanceWithdrawn; uint256 totalYBalanceWithdrawn; // To figure out amountXMin and amountYMin, we calculate how much X and Y underlying we have as liquidity for (uint256 i; i < activeBinIds.length; i++) { //amount of LBToken in each active bin uint256 LBTokenAmount = (PRECISION * lbToken.balanceOf(address(this), activeBinIds[i])) / (denominator); amounts[i] = LBTokenAmount; (uint256 binReserveX, uint256 binReserveY) = lbPair.getBin( uint24(activeBinIds[i]) ); totalXBalanceWithdrawn += (LBTokenAmount * binReserveX) / lbToken.totalSupply(activeBinIds[i]); totalYBalanceWithdrawn += (LBTokenAmount * binReserveY) / lbToken.totalSupply(activeBinIds[i]); } //remove the liquidity required (amountX, amountY) = ILBRouter(payable(joeRouter)).removeLiquidity( tokenX, tokenY, binStep, 0, 0, activeBinIds, amounts, address(this), block.timestamp ); //remove the ids from the userSet; this needs to occur if we are withdrawing all liquidity //or if we are rebalancing not each time liquidity is withdrawn if (denominator == PRECISION) { for (uint256 i; i < activeBinIds.length; i++) { _activeBins.remove(activeBinIds[i]); } } //emit event emit RemoveLiquidity(msg.sender, amountX, amountY); } /// @notice gives the allowances required for this strategy function _giveAllowances() internal { uint256 MAX_INT = 2 ** 256 - 1; tokenX.safeApprove(joeRouter, uint256(MAX_INT)); tokenY.safeApprove(joeRouter, uint256(MAX_INT)); tokenX.safeApprove(vault, uint256(MAX_INT)); tokenY.safeApprove(vault, uint256(MAX_INT)); //provide required lb token approvals lbToken.setApprovalForAll(address(joeRouter), true); } /// @notice remove allowances for this strategy function _removeAllowances() internal { tokenX.safeApprove(joeRouter, 0); tokenY.safeApprove(joeRouter, 0); tokenX.safeApprove(vault, 0); tokenY.safeApprove(vault, 0); //revoke required lb token approvals lbToken.setApprovalForAll(address(joeRouter), false); } /// @notice swaps tokens from the strategy /// @param amountIn the amount of token that needs to be swapped /// @param _swapForY is tokenX being swapped for tokenY function _swap( uint256 amountIn, bool _swapForY ) internal returns (uint256 amountOutReal) { IERC20[] memory tokenPath = new IERC20[](2); //set the required token paths if (_swapForY) { tokenPath[0] = tokenX; tokenPath[1] = tokenY; } else { tokenPath[0] = tokenY; tokenPath[1] = tokenX; } //compute the required bin step uint256[] memory pairBinSteps = new uint256[](1); pairBinSteps[0] = binStep; //perform the swapping of tokens amountOutReal = ILBRouter(joeRouter).swapExactTokensForTokens( amountIn, 0, pairBinSteps, tokenPath, address(this), block.timestamp ); } function _harvest() internal returns (uint256 amountXReceived, uint256 amountYReceived) { //collects pending rewards (amountXReceived, amountYReceived) = _collectRewards(); tokenX.safeTransfer(owner(), amountXReceived); tokenY.safeTransfer(owner(), amountYReceived); lastHarvest = block.timestamp; } /// @notice collects any availiable rewards from the pair /// @return amountXReceived tokenX amount received /// @return amountYReceived tokenY amount received function _collectRewards() internal returns (uint256 amountXReceived, uint256 amountYReceived) { (uint256 amountXBefore, uint256 amountYBefore) = getTotalAmounts(); uint256[] memory activeBinIds = getActiveBinIds(); (amountXReceived, amountYReceived) = lbPair.collectFees( address(this), activeBinIds ); (uint256 amountX, uint256 amountY) = getTotalAmounts(); //emit event fee's collected emit CollectRewards( lastHarvest, amountXBefore, amountYBefore, amountX, amountY ); } /// @notice helper safety check to see if distX and distY add to PRECISION /// @param _distribution distributionX or distributionY to check function _checkDistribution(uint256[] memory _distribution) internal pure { uint256 total; //loop over the distribution provided and make sure to sum to PRECISION for (uint256 i; i < _distribution.length; ++i) { total += _distribution[i]; } require(total == PRECISION, 'Strategy: Distribution incorrect'); } /// ----------------------------------------------------------- /// External functions /// ----------------------------------------------------------- /// @notice puts any available tokenX or tokenY to work /// @return amountX amount of token X added as liquidity /// @return amountY amount of token Y added as liquidity function earn() external onlyManager returns (uint256 amountX, uint256 amountY) { uint256 balanceX = tokenX.balanceOf(address(this)); uint256 balanceY = tokenY.balanceOf(address(this)); //gas saving check if there is idle funds in the contract require( balanceX > 1e3 || balanceY > 1e3, 'Vault: Insufficient idle funds in strategy' ); //require the amounts to be deposited into the correct bin distributions (amountX, amountY) = _calculateAmountsPerformSwap( deltaIds, balanceX, balanceY ); //use the funds in the strategy to add liquidty. if (amountX > 0 || amountY > 0) { _addLiquidity(amountX, amountY, 0, 0); } } /// @notice entrypoint for the vault to remove liquidity from the strategy /// @param denominator proportion of liquidity to remove /// @return amountX amount of tokenX removed from liquidity /// @return amountY amount of tokenY removed from liquidity function removeLiquidity( uint256 denominator ) external returns (uint256 amountX, uint256 amountY) { require(msg.sender == vault, 'Strategy: !vault'); (amountX, amountY) = _removeLiquidity(denominator); } /// @notice harvest the rewards from the strategy /// @return amountXReceived amount of tokenX received from harvest /// @return amountYReceived amount of tokenY received from harvest function harvest() external virtual returns (uint256 amountXReceived, uint256 amountYReceived) { (amountXReceived, amountYReceived) = _harvest(); } /// ----------------------------------------------------------- /// View functions /// ----------------------------------------------------------- /// @notice get the active bins that the strategy currently holds liquidity in. /// @return activeBinIds array of active binIds function getActiveBinIds() public view returns (uint256[] memory activeBinIds) { uint256 _userPositionNumber = strategyPositionNumber(); //init the new holder array activeBinIds = new uint256[](_userPositionNumber); //loop over user position index and retrieve the bin ids for (uint256 i; i < _userPositionNumber; ++i) { uint256 userPosition = strategyPositionAtIndex(i); activeBinIds[i] = userPosition; } return (activeBinIds); } /// @notice Calculates the vault's total holdings of tokenX and tokenY - in /// other words, how much of each token the vault would hold if it withdrew /// all its liquidity from Trader Joe Dex V2. /// @return totalX managed by the strategy /// @return totalY managed by the strategy function getTotalAmounts() public view returns (uint256 totalX, uint256 totalY) { //add currently active tokens supplied as liquidity (totalX, totalY) = LiquidityAmounts.getAmountsOf( address(this), strategyActiveBins(), address(lbPair) ); //add currently unused tokens totalX += getBalanceX(); totalY += getBalanceY(); } /// @notice checks if the deltaId's contain X liquidity /// @param _deltaIds to check /// @return hasXLiquidity if tokenX is required as liquidity by strategy function binHasXLiquidity( int256[] memory _deltaIds ) public pure returns (bool hasXLiquidity) { for (uint256 i; i < _deltaIds.length; i++) { if (_deltaIds[i] >= 0) { hasXLiquidity = true; break; } } } /// @notice checks if the deltaId's contain Y liquidity /// @param _deltaIds to check /// @return hasYLiquidity if tokenY is required as liquidity by strategy function binHasYLiquidity( int256[] memory _deltaIds ) public pure returns (bool hasYLiquidity) { for (uint256 i; i < _deltaIds.length; i++) { if (_deltaIds[i] <= 0) { hasYLiquidity = true; break; } } } /// @notice checks if there is rewards ready to be harvested from the pair /// @param _increasingBinIds strictly increasing binIds to check rewards in /// @return rewardsX amount of rewards available of tokenX /// @return rewardsY amount of rewards available of tokenY function rewardsAvailable( uint256[] memory _increasingBinIds ) external view returns (uint256 rewardsX, uint256 rewardsY) { require(_increasingBinIds.length > 0, 'Strat: Supply valid ids'); //pending fees requires strictly increasing ids (require sorting off chain) (rewardsX, rewardsY) = lbPair.pendingFees( address(this), _increasingBinIds ); } /// @notice Balance of tokenX in strategy not used in any position. /// @return tokenXAmount amount of tokenX idle in the strat function getBalanceX() public view returns (uint256) { return tokenX.balanceOf(address(this)); } /// @notice Balance of tokenY in strategy not used in any position. /// @return tokenYAmount amount of tokenY idle in the strat function getBalanceY() public view returns (uint256) { return tokenY.balanceOf(address(this)); } /// ----------------------------------------------------------- /// Manager / Owner functions /// ----------------------------------------------------------- /// @notice called as part of strat migration. /// Sends all the available funds back to the vault. function retireStrat() external { require(msg.sender == vault, '!vault'); //add currently active tokens supplied as liquidity (uint256 totalX, uint256 totalY) = LiquidityAmounts.getAmountsOf( address(this), strategyActiveBins(), address(lbPair) ); if (totalX > 0 || totalY > 0) { _removeLiquidity(PRECISION); } uint256 tokenXBal = tokenX.balanceOf(address(this)); uint256 tokenYBal = tokenY.balanceOf(address(this)); tokenX.transfer(vault, tokenXBal); tokenY.transfer(vault, tokenYBal); } /// @notice pauses deposits and withdraws all funds from third party systems. function panic() external onlyOwner { pause(); //add currently active tokens supplied as liquidity (uint256 totalX, uint256 totalY) = LiquidityAmounts.getAmountsOf( address(this), strategyActiveBins(), address(lbPair) ); //if there is liquidity remove it from dex and hold in strat if (totalX > 0 || totalY > 0) { _removeLiquidity(PRECISION); } } /// @notice pauses deposits and removes allowances function pause() public onlyOwner { _pause(); _removeAllowances(); } /// @notice allows deposits into third part systems function unpause() external onlyOwner { _unpause(); _giveAllowances(); } /// @notice allows removal of tokens if necessary function removeTokens(address token) external onlyOwner { uint256 tokenBal = ERC20(token).balanceOf(address(this)); if (tokenBal > 0) { ERC20(token).transfer(vault, tokenBal); } } /// ----------------------------------------------------------- /// Rebalance functions /// ----------------------------------------------------------- /// @notice point of call to execute a rebalance of the strategy with the same params /// @return amountX total amountX supplied after the rebalance /// @return amountY total amountY supplied after the rebalance function executeRebalance() external onlyManager returns (uint256 amountX, uint256 amountY) { (amountX, amountY) = executeRebalance( deltaIds, distributionX, distributionY, idSlippage ); } /// @notice point of call to execute a rebalance of the strategy with new params /// @param _deltaIds the distribution of liquidity around the active bin /// @param _distributionX the distribution of tokenX liquidity around the active bin /// @param _distributionY the distribution of tokenY liquidity around the active bin /// @param _idSlippage slippage of bins acceptable /// @return amountX total amountX supplied after the rebalance /// @return amountY total amountY supplied after the rebalance function executeRebalance( int256[] memory _deltaIds, uint256[] memory _distributionX, uint256[] memory _distributionY, uint256 _idSlippage ) public onlyManager returns (uint256 amountX, uint256 amountY) { //ensure that we are keeping to the 50 bin limit require( _deltaIds.length < 50, 'Strategy: Bins shall be limited to <50' ); //check parameters are equal in length require( _deltaIds.length == _distributionX.length && _distributionX.length == _distributionY.length, 'Strategy: Unbalanced params' ); //check the distributions are correct if (binHasXLiquidity(_deltaIds)) { _checkDistribution(_distributionX); } if (binHasYLiquidity(_deltaIds)) { _checkDistribution(_distributionY); } (uint256 amountXBefore, uint256 amountYBefore) = getTotalAmounts(); //remove any liquidity from joeRouter and clear active bins set if (strategyPositionNumber() > 0) { _removeLiquidity(PRECISION); } //rebalance the strategy params deltaIds = _deltaIds; distributionX = _distributionX; distributionY = _distributionY; idSlippage = _idSlippage; //calculate new amounts to deposit if existing deposits if (amountXBefore > 0 || amountYBefore > 0) { (amountX, amountY) = _calculateAmountsPerformSwap( _deltaIds, tokenX.balanceOf(address(this)), tokenY.balanceOf(address(this)) ); } //only add liquidity if strategy has funds if (amountX > 0 || amountY > 0) { _addLiquidity(amountX, amountY, 0, 0); } (amountX, amountY) = getTotalAmounts(); //emit event emit Rebalance(amountXBefore, amountYBefore, amountX, amountY); } /// @notice aligns the tokens to the required strategy deltas /// performs swaps on tokens not required for the bin offsets /// @param _deltaIds the delta ids of the current strategy /// @param _amountX the amount of tokenX in the strategy /// @param _amountY the amount of tokenY in the strategy /// @return amountX amount of tokenX to add as liquidity /// @return amountY amount of tokenY to add as liquidity function _calculateAmountsPerformSwap( int256[] memory _deltaIds, uint256 _amountX, uint256 _amountY ) internal returns (uint256 amountX, uint256 amountY) { //only token y liquidity if (!binHasXLiquidity(_deltaIds) && binHasYLiquidity(_deltaIds)) { //swap X for Y _swap(_amountX, true); amountY = tokenY.balanceOf(address(this)); amountX = 0; } //only token x liquidity if (!binHasYLiquidity(_deltaIds) && binHasXLiquidity(_deltaIds)) { //swap Y for X _swap(_amountY, false); amountX = tokenX.balanceOf(address(this)); amountY = 0; } //if has both token x and token y liquidity if (binHasYLiquidity(_deltaIds) && binHasXLiquidity(_deltaIds)) { // Want to balance the dollar value on both sides (that way we are agnostic to direction and can gather volume on both sides) if (_amountX == 0) { //swap y for x _swap((50 * _amountY) / 100, false); } if (_amountY == 0) { //swap x for y _swap((50 * _amountX) / 100, true); } //set the final amounts for deposits amountX = tokenX.balanceOf(address(this)); amountY = tokenY.balanceOf(address(this)); } } /// @notice Returns whether this contract implements the interface defined by /// `interfaceId` (true) or not (false) /// @param _interfaceId The interface identifier /// @return Whether the interface is supported (true) or not (false) function supportsInterface(bytes4 _interfaceId) public pure returns (bool) { return _interfaceId == type(ILBToken).interfaceId || _interfaceId == type(IERC165).interfaceId; } }
{ "evmVersion": "london", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 1000 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
[{"inputs":[{"internalType":"address","name":"_joeRouter","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_vault","type":"address"},{"components":[{"internalType":"contract IERC20","name":"tokenX","type":"address"},{"internalType":"contract IERC20","name":"tokenY","type":"address"},{"internalType":"address","name":"pair","type":"address"},{"internalType":"uint16","name":"binStep","type":"uint16"},{"internalType":"int256[]","name":"deltaIds","type":"int256[]"},{"internalType":"uint256[]","name":"distributionX","type":"uint256[]"},{"internalType":"uint256[]","name":"distributionY","type":"uint256[]"},{"internalType":"uint256","name":"idSlippage","type":"uint256"}],"internalType":"struct ILBStrategy.StrategyParameters","name":"_strategyParams","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"prod1","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"Math512Bits__MulDivOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"SafeCast__Exceeds24Bits","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"lastHarvest","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountXBefore","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountYBefore","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountXAfter","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountYAfter","type":"uint256"}],"name":"CollectRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"FeeTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amountXBefore","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountYBefore","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountXAfter","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountYAfter","type":"uint256"}],"name":"Rebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"joeRouter","type":"address"}],"name":"SetJoeRouter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"keeper","type":"address"}],"name":"SetKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"beforeDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256[]","name":"_deltaIds","type":"int256[]"}],"name":"binHasXLiquidity","outputs":[{"internalType":"bool","name":"hasXLiquidity","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256[]","name":"_deltaIds","type":"int256[]"}],"name":"binHasYLiquidity","outputs":[{"internalType":"bool","name":"hasYLiquidity","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"binStep","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256[]","name":"proposedDeltas","type":"int256[]"},{"internalType":"uint256","name":"activeId","type":"uint256"}],"name":"checkProposedBinLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"deltaIds","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"distributionX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"distributionY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"earn","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256[]","name":"_deltaIds","type":"int256[]"},{"internalType":"uint256[]","name":"_distributionX","type":"uint256[]"},{"internalType":"uint256[]","name":"_distributionY","type":"uint256[]"},{"internalType":"uint256","name":"_idSlippage","type":"uint256"}],"name":"executeRebalance","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"executeRebalance","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getActiveBinIds","outputs":[{"internalType":"uint256[]","name":"activeBinIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalanceX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalanceY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalAmounts","outputs":[{"internalType":"uint256","name":"totalX","type":"uint256"},{"internalType":"uint256","name":"totalY","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvest","outputs":[{"internalType":"uint256","name":"amountXReceived","type":"uint256"},{"internalType":"uint256","name":"amountYReceived","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"idSlippage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"joeRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastHarvest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lbPair","outputs":[{"internalType":"contract ILBPair","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lbToken","outputs":[{"internalType":"contract ILBToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"panic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"removeTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"retireStrat","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_increasingBinIds","type":"uint256[]"}],"name":"rewardsAvailable","outputs":[{"internalType":"uint256","name":"rewardsX","type":"uint256"},{"internalType":"uint256","name":"rewardsY","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_joeRouter","type":"address"}],"name":"setJoeRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keeper","type":"address"}],"name":"setKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strategyActiveBins","outputs":[{"internalType":"uint256[]","name":"activeBins","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"strategyPositionAtIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategyPositionNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"tokenX","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenY","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101406040523480156200001257600080fd5b506040516200531238038062005312833981016040819052620000359162000a53565b828483620000433362000243565b6000805460ff60a01b19169055600180546001600160a01b03199081166001600160a01b0395861617909155600280549091169284169290921790915516608090815281015151603211620000df5760405162461bcd60e51b815260206004820152601760248201527f53747261746567793a20546f6f206d616e792062696e7300000000000000000060448201526064015b60405180910390fd5b8060a00151518160800151511480156200010257508060c00151518160a0015151145b620001505760405162461bcd60e51b815260206004820152601b60248201527f53747261746567793a20556e62616c616e63656420706172616d7300000000006044820152606401620000d6565b6080810151620001609062000293565b15620001765760a08101516200017690620002eb565b6080810151620001869062000396565b156200019c5760c08101516200019c90620002eb565b80516001600160a01b0390811660a052602080830151821660c052604083018051831660e0525190911661010052606082015161ffff166101205260808201518051620001ee926004920190620008d3565b5060e081015160075560a081015180516200021291600591602090910190620008d3565b5060c081015180516200022e91600691602090910190620008d3565b5062000239620003e8565b5050505062000cd1565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000805b8251811015620002e5576000838281518110620002b857620002b862000baf565b602002602001015112620002d05760019150620002e5565b80620002dc8162000bdb565b91505062000297565b50919050565b6000805b825181101562000338578281815181106200030e576200030e62000baf565b60200260200101518262000323919062000bf7565b9150620003308162000bdb565b9050620002ef565b50670de0b6b3a76400008114620003925760405162461bcd60e51b815260206004820181905260248201527f53747261746567793a20446973747269627574696f6e20696e636f72726563746044820152606401620000d6565b5050565b6000805b8251811015620002e5576000838281518110620003bb57620003bb62000baf565b602002602001015113620003d35760019150620002e5565b80620003df8162000bdb565b9150506200039a565b60025460a0516000199162000417916001600160a01b0390811691168362000502602090811b62001b1f17901c565b60025460c05162000442916001600160a01b0391821691168362000502602090811b62001b1f17901c565b6200046b6080518260a0516001600160a01b03166200050260201b62001b1f179092919060201c565b620004946080518260c0516001600160a01b03166200050260201b62001b1f179092919060201c565b6101005160025460405163a22cb46560e01b81526001600160a01b0391821660048201526001602482015291169063a22cb46590604401600060405180830381600087803b158015620004e657600080fd5b505af1158015620004fb573d6000803e3d6000fd5b5050505050565b801580620005805750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa15801562000558573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200057e919062000c13565b155b620005f45760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401620000d6565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b179091526200064c9185916200065116565b505050565b6000620006ad826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166200072f60201b62001cb5179092919060201c565b8051909150156200064c5780806020019051810190620006ce919062000c2d565b6200064c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401620000d6565b606062000740848460008562000748565b949350505050565b606082471015620007ab5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401620000d6565b600080866001600160a01b03168587604051620007c9919062000c7e565b60006040518083038185875af1925050503d806000811462000808576040519150601f19603f3d011682016040523d82523d6000602084013e6200080d565b606091505b50909250905062000821878383876200082c565b979650505050505050565b60608315620008a057825160000362000898576001600160a01b0385163b620008985760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620000d6565b508162000740565b620007408383815115620008b75781518083602001fd5b8060405162461bcd60e51b8152600401620000d6919062000c9c565b82805482825590600052602060002090810192821562000911579160200282015b8281111562000911578251825591602001919060010190620008f4565b506200091f92915062000923565b5090565b5b808211156200091f576000815560010162000924565b6001600160a01b03811681146200095057600080fd5b50565b805162000960816200093a565b919050565b634e487b7160e01b600052604160045260246000fd5b60405161010081016001600160401b0381118282101715620009a157620009a162000965565b60405290565b805161ffff811681146200096057600080fd5b600082601f830112620009cc57600080fd5b815160206001600160401b0380831115620009eb57620009eb62000965565b8260051b604051601f19603f8301168101818110848211171562000a135762000a1362000965565b60405293845285810183019383810192508785111562000a3257600080fd5b83870191505b84821015620008215781518352918301919083019062000a38565b6000806000806080858703121562000a6a57600080fd5b845162000a77816200093a565b602086015190945062000a8a816200093a565b604086015190935062000a9d816200093a565b60608601519092506001600160401b038082111562000abb57600080fd5b90860190610100828903121562000ad157600080fd5b62000adb6200097b565b62000ae68362000953565b815262000af66020840162000953565b602082015262000b096040840162000953565b604082015262000b1c60608401620009a7565b606082015260808301518281111562000b3457600080fd5b62000b428a828601620009ba565b60808301525060a08301518281111562000b5b57600080fd5b62000b698a828601620009ba565b60a08301525060c08301518281111562000b8257600080fd5b62000b908a828601620009ba565b60c08301525060e083015160e082015280935050505092959194509250565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820162000bf05762000bf062000bc5565b5060010190565b8082018082111562000c0d5762000c0d62000bc5565b92915050565b60006020828403121562000c2657600080fd5b5051919050565b60006020828403121562000c4057600080fd5b8151801515811462000c5157600080fd5b9392505050565b60005b8381101562000c7557818101518382015260200162000c5b565b50506000910152565b6000825162000c9281846020870162000c58565b9190910192915050565b602081526000825180602084015262000cbd81604085016020870162000c58565b601f01601f19169190910160400192915050565b60805160a05160c05160e051610100516101205161447762000e9b600039600081816105710152818161263701528181612f1801526138b301526000818161038501528181611ec60152818161225e0152818161240d015281816124d5015261296f01526000818161045a015281816108c1015281816109c60152818161235901528181612d7d01526134f501526000818161052f01528181610bdf01528181610ed0015281816114140152818161182e0152818161199801528181611dcc01528181611e5601528181611f950152818161261501528181612872015281816128fe01528181612aa801528181612cf001528181612ee90152818161378f01526137e80152600081816103250152818161079e01528181610e500152818161137e01528181611798015281816118e201528181611d9201528181611e0201528181611f4f015281816125f301528181612837015281816128a901528181612b6201528181612c6501528181612eba0152818161373b015261383c0152600081816105f201528181611153015281816116d8015281816118b00152818161196901528181611a9e01528181611e2401528181611e78015281816128cb015261292001526144776000f3fe608060405234801561001057600080fd5b50600436106102de5760003560e01c80637d3db06711610186578063b86e9b8a116100e3578063d9c3ac3c11610097578063fb61778711610071578063fb617787146105e5578063fbfa77cf146105ed578063fc50baf51461061457600080fd5b8063d9c3ac3c146105b6578063f1a392da146105c9578063f2fde38b146105d257600080fd5b8063c5bf3832116100c8578063c5bf38321461056c578063d389800f146105a6578063d69d1dac146105ae57600080fd5b8063b86e9b8a14610551578063c4a7761e1461056457600080fd5b80639bb71aef1161013a578063a808ac661161011f578063a808ac6614610502578063aced166114610517578063b7d19fc41461052a57600080fd5b80639bb71aef146104e75780639c8f9f23146104ef57600080fd5b80638afe5a6d1161016b5780638afe5a6d146104ba5780638befcc6f146104cd5780638da5cb5b146104d657600080fd5b80637d3db067146104aa5780638456cb59146104b257600080fd5b80634641257d1161023f5780635bbe5e10116101f3578063651f1c7b116101cd578063651f1c7b1461047c578063715018a61461048f578063748747e61461049757600080fd5b80635bbe5e10146104305780635c975abb14610443578063625af7671461045557600080fd5b80634700d305116102245780634700d30514610415578063573fef0a1461031e57806359f571e81461041d57600080fd5b80634641257d146103fa57806346e9bfdb1461040257600080fd5b806338a26bfb116102965780633ec4515d1161027b5780633ec4515d146103c25780633f4ba83a146103ca57806343c23620146103d257600080fd5b806338a26bfb146103a75780633b6bc1a7146103ba57600080fd5b806316dc165b116102c757806316dc165b14610320578063221918661461035f57806334f1f9b11461038057600080fd5b806301ffc9a7146102e3578063026b49a71461030b575b600080fd5b6102f66102f1366004613cca565b610627565b60405190151581526020015b60405180910390f35b61031e610319366004613cf4565b610690565b005b6103477f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610302565b61037261036d366004613d1d565b610755565b604051908152602001610302565b6103477f000000000000000000000000000000000000000000000000000000000000000081565b6103726103b5366004613d1d565b610776565b610372610786565b610372610817565b61031e610823565b6103e56103e0366004613e0c565b61083d565b60408051928352602083019190915201610302565b6103e5610942565b6102f6610410366004613e0c565b610956565b61031e6109a5565b600254610347906001600160a01b031681565b61037261043e366004613d1d565b610a1a565b600054600160a01b900460ff166102f6565b6103477f000000000000000000000000000000000000000000000000000000000000000081565b61037261048a366004613e41565b610a27565b61031e610aaa565b61031e6104a5366004613cf4565b610abc565b610372610bc7565b61031e610c16565b6103e56104c8366004613e86565b610c2e565b61037260075481565b6000546001600160a01b0316610347565b6103e5610fd2565b6103e56104fd366004613d1d565b611145565b61050a6111c9565b6040516103029190613f51565b600154610347906001600160a01b031681565b6103477f000000000000000000000000000000000000000000000000000000000000000081565b6102f661055f366004613e0c565b611268565b6103e56112b1565b6105937f000000000000000000000000000000000000000000000000000000000000000081565b60405161ffff9091168152602001610302565b6103e56112f1565b61050a61158d565b6103726105c4366004613d1d565b61162d565b61037260035481565b61031e6105e0366004613cf4565b61163d565b61031e6116cd565b6103477f000000000000000000000000000000000000000000000000000000000000000081565b61031e610622366004613cf4565b611a0c565b60006001600160e01b031982167fdfad3d8c00000000000000000000000000000000000000000000000000000000148061068a57506001600160e01b031982167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b610698611cc4565b6001600160a01b0381166106f35760405162461bcd60e51b815260206004820152601760248201527f53747261744d616e616765723a2030206164647265737300000000000000000060448201526064015b60405180910390fd5b6002805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527fa74fa1653ae077d0f95003e851b92a7526da5053b12a1e052f360333fb49cb80906020015b60405180910390a150565b6004818154811061076557600080fd5b600091825260209091200154905081565b6006818154811061076557600080fd5b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a08231906024015b602060405180830381865afa1580156107ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108129190613f64565b905090565b60006108126008611d1e565b61082b611cc4565b610833611d28565b61083b611d7d565b565b60008060008351116108915760405162461bcd60e51b815260206004820152601760248201527f53747261743a20537570706c792076616c69642069647300000000000000000060448201526064016106ea565b6040517ff7cff1f80000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f7cff1f8906108f89030908790600401613f7d565b6040805180830381865afa158015610914573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109389190613f9f565b9094909350915050565b60008061094d611f20565b90939092509050565b6000805b825181101561099f57600083828151811061097757610977613fc3565b60200260200101511261098d576001915061099f565b8061099781613fef565b91505061095a565b50919050565b6109ad611cc4565b6109b5610c16565b6000806109ea306109c461158d565b7f0000000000000000000000000000000000000000000000000000000000000000611fc4565b9150915060008211806109fd5750600081115b15610a1657610a13670de0b6b3a76400006121f3565b50505b5050565b600061068a60088361275b565b60008060005b8451811015610a8f576000858281518110610a4a57610a4a613fc3565b602002602001015185610a5d9190614008565b9050610a6a60088261276e565b610a7c57610a79600184614030565b92505b5080610a8781613fef565b915050610a2d565b50610a98610817565b610aa29082614030565b949350505050565b610ab2611cc4565b61083b6000612786565b6000546001600160a01b0316331480610adf57506001546001600160a01b031633145b610b165760405162461bcd60e51b815260206004820152600860248201526710b6b0b730b3b2b960c11b60448201526064016106ea565b6001600160a01b038116610b6c5760405162461bcd60e51b815260206004820152601760248201527f53747261744d616e616765723a2030206164647265737300000000000000000060448201526064016106ea565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527fefb5cfa1a8690c124332ab93324539c5c9c4be03f28aeb8be86f2d8a0c9fb99b9060200161074a565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a08231906024016107d1565b610c1e611cc4565b610c266127e3565b61083b612826565b600080610c436000546001600160a01b031690565b6001600160a01b0316336001600160a01b03161480610c6c57506001546001600160a01b031633145b610ca35760405162461bcd60e51b815260206004820152600860248201526710b6b0b730b3b2b960c11b60448201526064016106ea565b6032865110610d1a5760405162461bcd60e51b815260206004820152602660248201527f53747261746567793a2042696e73207368616c6c206265206c696d697465642060448201527f746f203c3530000000000000000000000000000000000000000000000000000060648201526084016106ea565b84518651148015610d2c575083518551145b610d785760405162461bcd60e51b815260206004820152601b60248201527f53747261746567793a20556e62616c616e63656420706172616d73000000000060448201526064016106ea565b610d8186610956565b15610d8f57610d8f856129c9565b610d9886611268565b15610da657610da6846129c9565b600080610db16112b1565b915091506000610dbf610817565b1115610dd957610dd6670de0b6b3a76400006121f3565b50505b8751610dec9060049060208b0190613c73565b508651610e009060059060208a0190613c73565b508551610e14906006906020890190613c73565b50600785905581151580610e285750600081115b15610f4e576040516370a0823160e01b8152306004820152610f489089906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610e97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ebb9190613f64565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610f1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f439190613f64565b612a65565b90945092505b6000841180610f5d5750600083115b15610f7257610f6f8484600080612d6e565b50505b610f7a6112b1565b60408051858152602081018590529081018390526060810182905291955093507f802a0d65ee1ffb3e6af96deafe1fc3e6402b2672a2f4b79d1b23c4a06c998f349060800160405180910390a1505094509492505050565b600080610fe76000546001600160a01b031690565b6001600160a01b0316336001600160a01b0316148061101057506001546001600160a01b031633145b6110475760405162461bcd60e51b815260206004820152600860248201526710b6b0b730b3b2b960c11b60448201526064016106ea565b61094d600480548060200260200160405190810160405280929190818152602001828054801561109657602002820191906000526020600020905b815481526020019060010190808311611082575b505050505060058054806020026020016040519081016040528092919081815260200182805480156110e757602002820191906000526020600020905b8154815260200190600101908083116110d3575b5050505050600680548060200260200160405190810160405280929190818152602001828054801561113857602002820191906000526020600020905b815481526020019060010190808311611124575b5050505050600754610c2e565b600080336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111c05760405162461bcd60e51b815260206004820152601060248201527f53747261746567793a20217661756c740000000000000000000000000000000060448201526064016106ea565b610938836121f3565b606060006111d5610817565b90508067ffffffffffffffff8111156111f0576111f0613d36565b604051908082528060200260200182016040528015611219578160200160208202803683370190505b50915060005b8181101561126357600061123282610a1a565b90508084838151811061124757611247613fc3565b60209081029190910101525061125c81613fef565b905061121f565b505090565b6000805b825181101561099f57600083828151811061128957611289613fc3565b60200260200101511361129f576001915061099f565b806112a981613fef565b91505061126c565b6000806112c0306109c461158d565b90925090506112cd610786565b6112d79083614030565b91506112e1610bc7565b6112eb9082614030565b90509091565b6000806113066000546001600160a01b031690565b6001600160a01b0316336001600160a01b0316148061132f57506001546001600160a01b031633145b6113665760405162461bcd60e51b815260206004820152600860248201526710b6b0b730b3b2b960c11b60448201526064016106ea565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156113cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f19190613f64565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561145b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061147f9190613f64565b90506103e882118061149257506103e881115b6115045760405162461bcd60e51b815260206004820152602a60248201527f5661756c743a20496e73756666696369656e742069646c652066756e6473206960448201527f6e2073747261746567790000000000000000000000000000000000000000000060648201526084016106ea565b61155f600480548060200260200160405190810160405280929190818152602001828054801561155357602002820191906000526020600020905b81548152602001906001019080831161153f575b50505050508383612a65565b9094509250831515806115725750600083115b15611587576115848484600080612d6e565b50505b50509091565b60606115996008611d1e565b67ffffffffffffffff8111156115b1576115b1613d36565b6040519080825280602002602001820160405280156115da578160200160208202803683370190505b50905060005b6115ea6008611d1e565b811015611629576115fa81610a1a565b82828151811061160c5761160c613fc3565b60209081029190910101528061162181613fef565b9150506115e0565b5090565b6005818154811061076557600080fd5b611645611cc4565b6001600160a01b0381166116c15760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016106ea565b6116ca81612786565b50565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146117455760405162461bcd60e51b815260206004820152600660248201527f217661756c74000000000000000000000000000000000000000000000000000060448201526064016106ea565b600080611754306109c461158d565b9150915060008211806117675750600081115b156117805761177d670de0b6b3a76400006121f3565b50505b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156117e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180b9190613f64565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611875573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118999190613f64565b60405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018590529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af115801561192d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119519190614043565b5060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af11580156119e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a059190614043565b5050505050565b611a14611cc4565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611a5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a7f9190613f64565b90508015610a165760405163a9059cbb60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301526024820183905283169063a9059cbb906044016020604051808303816000875af1158015611af6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b1a9190614043565b505050565b801580611bb257506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611b8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bb09190613f64565b155b611c245760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e63650000000000000000000060648201526084016106ea565b6040516001600160a01b038316602482015260448101829052611b1a9084907f095ea7b300000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990931692909217909152613276565b6060610aa2848460008561335b565b6000546001600160a01b0316331461083b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016106ea565b600061068a825490565b611d3061344d565b6000805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b60025460001990611dbb906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911683611b1f565b600254611df5906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911683611b1f565b611e496001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083611b1f565b611e9d6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083611b1f565b60025460405163a22cb46560e01b81526001600160a01b039182166004820152600160248201527f00000000000000000000000000000000000000000000000000000000000000009091169063a22cb46590604401600060405180830381600087803b158015611f0c57600080fd5b505af1158015611a05573d6000803e3d6000fd5b600080611f2b6134a6565b9092509050611f76611f456000546001600160a01b031690565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690846135dd565b611fbc611f8b6000546001600160a01b031690565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690836135dd565b426003559091565b60008060005b84518110156121ea576000611ff7868381518110611fea57611fea613fc3565b602002602001015161360d565b6040517efdd58e0000000000000000000000000000000000000000000000000000000081526001600160a01b03898116600483015262ffffff8316602483015291925060009187169062fdd58e90604401602060405180830381865afa158015612065573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120899190613f64565b6040517f0abe968800000000000000000000000000000000000000000000000000000000815262ffffff8416600482015290915060009081906001600160a01b03891690630abe9688906024016040805180830381865afa1580156120f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121169190613f9f565b6040517fbd85b03900000000000000000000000000000000000000000000000000000000815262ffffff8716600482015291935091506000906001600160a01b038a169063bd85b03990602401602060405180830381865afa158015612180573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121a49190613f64565b90506121b1848483613654565b6121bb9089614030565b97506121c8848383613654565b6121d29088614030565b96505050505050806121e390613fef565b9050611fca565b50935093915050565b60008060006122006111c9565b90506000815167ffffffffffffffff81111561221e5761221e613d36565b604051908082528060200260200182016040528015612247578160200160208202803683370190505b50905060008060005b84518110156125b1576000887f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031662fdd58e3089868151811061229d5761229d613fc3565b60200260200101516040518363ffffffff1660e01b81526004016122d69291906001600160a01b03929092168252602082015260400190565b602060405180830381865afa1580156122f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123179190613f64565b61232990670de0b6b3a7640000614065565b6123339190614092565b90508085838151811061234857612348613fc3565b6020026020010181815250506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630abe968889868151811061239857612398613fc3565b60200260200101516040518263ffffffff1660e01b81526004016123c7919062ffffff91909116815260200190565b6040805180830381865afa1580156123e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124079190613f9f565b915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bd85b03989868151811061244c5761244c613fc3565b60200260200101516040518263ffffffff1660e01b815260040161247291815260200190565b602060405180830381865afa15801561248f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124b39190613f64565b6124bd8385614065565b6124c79190614092565b6124d19087614030565b95507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bd85b03989868151811061251457612514613fc3565b60200260200101516040518263ffffffff1660e01b815260040161253a91815260200190565b602060405180830381865afa158015612557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257b9190613f64565b6125858285614065565b61258f9190614092565b6125999086614030565b945050505080806125a990613fef565b915050612250565b506002546040517fc22159b60000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063c22159b69061266c907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f00000000000000000000000000000000000000000000000000000000000000009060009081908c908c90309042906004016140b4565b60408051808303816000875af115801561268a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ae9190613f9f565b9096509450670de0b6b3a763ffff1987016127115760005b845181101561270f576126fc8582815181106126e4576126e4613fc3565b6020026020010151600861367e90919063ffffffff16565b508061270781613fef565b9150506126c6565b505b60408051338152602081018890529081018690527f0fbf06c058b90cb038a618f8c2acbf6145f8b3570fd1fa56abb8f0f3f05b36e89060600160405180910390a150505050915091565b6000612767838361368a565b9392505050565b60008181526001830160205260408120541515612767565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6127eb6136b4565b6000805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611d603390565b600254612861906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691166000611b1f565b60025461289c906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691166000611b1f565b6128f16001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000006000611b1f565b6129466001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000006000611b1f565b60025460405163a22cb46560e01b81526001600160a01b039182166004820152600060248201527f00000000000000000000000000000000000000000000000000000000000000009091169063a22cb46590604401600060405180830381600087803b1580156129b557600080fd5b505af1158015610a13573d6000803e3d6000fd5b6000805b8251811015612a0d578281815181106129e8576129e8613fc3565b6020026020010151826129fb9190614030565b9150612a0681613fef565b90506129cd565b50670de0b6b3a76400008114610a165760405162461bcd60e51b815260206004820181905260248201527f53747261746567793a20446973747269627574696f6e20696e636f727265637460448201526064016106ea565b600080612a7185610956565b158015612a825750612a8285611268565b15612b2257612a9284600161370e565b506040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b1b9190613f64565b9050600091505b612b2b85611268565b158015612b3c5750612b3c85610956565b15612bdc57612b4c83600061370e565b506040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612bb1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bd59190613f64565b9150600090505b612be585611268565b8015612bf55750612bf585610956565b15612d665783600003612c2557612c236064612c12856032614065565b612c1c9190614092565b600061370e565b505b82600003612c5057612c4e6064612c3d866032614065565b612c479190614092565b600161370e565b505b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612cb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cd89190613f64565b6040516370a0823160e01b81523060048201529092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612d3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d639190613f64565b90505b935093915050565b606080612d796136b4565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631b05b83e6040518163ffffffff1660e01b8152600401606060405180830381865afa158015612dd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dfd9190614123565b925050506032612e5d6004805480602002602001604051908101604052809291908181526020018280548015612e5257602002820191906000526020600020905b815481526020019060010190808311612e3e575b505050505083610a27565b10612eaa5760405162461bcd60e51b815260206004820152601c60248201527f53747261746567793a20526571756972657320526562616c616e63650000000060448201526064016106ea565b6000604051806101c001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f000000000000000000000000000000000000000000000000000000000000000061ffff16815260200189815260200188815260200187815260200186815260200183815260200160075481526020016004805480602002602001604051908101604052809291908181526020018280548015612fb257602002820191906000526020600020905b815481526020019060010190808311612f9e575b50505050508152602001600580548060200260200160405190810160405280929190818152602001828054801561300857602002820191906000526020600020905b815481526020019060010190808311612ff4575b50505050508152602001600680548060200260200160405190810160405280929190818152602001828054801561305e57602002820191906000526020600020905b81548152602001906001019080831161304a575b50505091835250503060208201524260409182015260025490517fe324a3e40000000000000000000000000000000000000000000000000000000081529192506001600160a01b03169063e324a3e4906130bc908490600401614151565b6000604051808303816000875af11580156130db573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261310391908101906142c3565b90945092506000805b845181101561317c5784818151811061312757613127613fc3565b60200260200101518261313a9190614030565b915061316986828151811061315157613151613fc3565b6020026020010151600861399490919063ffffffff16565b508061317481613fef565b91505061310c565b5060326131896008611d1e565b106132225760405162461bcd60e51b815260206004820152605060248201527f53747261746567793a20546f6f206d616e792062696e7320616674657220616460448201527f643b206d616e6167657220636865636b2062696e20736c69707061676520616e60648201527f642063616c6c20726562616c616e636500000000000000000000000000000000608482015260a4016106ea565b60408051338152602081018b9052908101899052606081018290527fbeb3885786d637a474cbc287c0a44587231633a077f0bd30354d5a4b18996fce9060800160405180910390a150505094509492505050565b60006132cb826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611cb59092919063ffffffff16565b805190915015611b1a57808060200190518101906132e99190614043565b611b1a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016106ea565b6060824710156133d35760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016106ea565b600080866001600160a01b031685876040516133ef919061434b565b60006040518083038185875af1925050503d806000811461342c576040519150601f19603f3d011682016040523d82523d6000602084013e613431565b606091505b5091509150613442878383876139a0565b979650505050505050565b600054600160a01b900460ff1661083b5760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f742070617573656400000000000000000000000060448201526064016106ea565b6000806000806134b46112b1565b9150915060006134c26111c9565b6040517f225b20b90000000000000000000000000000000000000000000000000000000081529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063225b20b99061352c9030908590600401613f7d565b60408051808303816000875af115801561354a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356e9190613f9f565b909550935060008061357e6112b1565b60035460408051918252602082018990528101879052606081018390526080810182905291935091507fd9e37493c971e3a52b9a6a43c700f1b92f6579e50559027a4991d83b580183d39060a00160405180910390a150505050509091565b6040516001600160a01b038316602482015260448101829052611b1a90849063a9059cbb60e01b90606401611c69565b8062ffffff8116811461364f576040517f76949f3c000000000000000000000000000000000000000000000000000000008152600481018390526024016106ea565b919050565b60008060006136638686613a19565b915091506136748686868585613a38565b9695505050505050565b60006127678383613b07565b60008260000182815481106136a1576136a1613fc3565b9060005260206000200154905092915050565b600054600160a01b900460ff161561083b5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a207061757365640000000000000000000000000000000060448201526064016106ea565b6040805160028082526060820183526000928392919060208301908036833701905050905082156137e6577f00000000000000000000000000000000000000000000000000000000000000008160008151811061376d5761376d613fc3565b60200260200101906001600160a01b031690816001600160a01b0316815250507f0000000000000000000000000000000000000000000000000000000000000000816001815181106137c1576137c1613fc3565b60200260200101906001600160a01b031690816001600160a01b03168152505061388f565b7f00000000000000000000000000000000000000000000000000000000000000008160008151811061381a5761381a613fc3565b60200260200101906001600160a01b031690816001600160a01b0316815250507f00000000000000000000000000000000000000000000000000000000000000008160018151811061386e5761386e613fc3565b60200260200101906001600160a01b031690816001600160a01b0316815250505b604080516001808252818301909252600091602080830190803683370190505090507f000000000000000000000000000000000000000000000000000000000000000061ffff16816000815181106138e9576138e9613fc3565b60209081029190910101526002546040517f6d0ff4950000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690636d0ff495906139489088906000908690889030904290600401614367565b6020604051808303816000875af1158015613967573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061398b9190613f64565b95945050505050565b60006127678383613bfa565b60608315613a0f578251600003613a08576001600160a01b0385163b613a085760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016106ea565b5081610aa2565b610aa28383613c49565b6000806000198385098385029250828110838203039150509250929050565b600081600003613a5957838381613a5157613a5161407c565b04905061398b565b838210613a9c576040517fcad6a85600000000000000000000000000000000000000000000000000000000815260048101839052602481018590526044016106ea565b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b60008181526001830160205260408120548015613bf0576000613b2b6001836143e5565b8554909150600090613b3f906001906143e5565b9050818114613ba4576000866000018281548110613b5f57613b5f613fc3565b9060005260206000200154905080876000018481548110613b8257613b82613fc3565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613bb557613bb56143f8565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061068a565b600091505061068a565b6000818152600183016020526040812054613c415750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561068a565b50600061068a565b815115613c595781518083602001fd5b8060405162461bcd60e51b81526004016106ea919061440e565b828054828255906000526020600020908101928215613cae579160200282015b82811115613cae578251825591602001919060010190613c93565b506116299291505b808211156116295760008155600101613cb6565b600060208284031215613cdc57600080fd5b81356001600160e01b03198116811461276757600080fd5b600060208284031215613d0657600080fd5b81356001600160a01b038116811461276757600080fd5b600060208284031215613d2f57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715613d7557613d75613d36565b604052919050565b600067ffffffffffffffff821115613d9757613d97613d36565b5060051b60200190565b600082601f830112613db257600080fd5b81356020613dc7613dc283613d7d565b613d4c565b82815260059290921b84018101918181019086841115613de657600080fd5b8286015b84811015613e015780358352918301918301613dea565b509695505050505050565b600060208284031215613e1e57600080fd5b813567ffffffffffffffff811115613e3557600080fd5b610aa284828501613da1565b60008060408385031215613e5457600080fd5b823567ffffffffffffffff811115613e6b57600080fd5b613e7785828601613da1565b95602094909401359450505050565b60008060008060808587031215613e9c57600080fd5b843567ffffffffffffffff80821115613eb457600080fd5b613ec088838901613da1565b95506020870135915080821115613ed657600080fd5b613ee288838901613da1565b94506040870135915080821115613ef857600080fd5b50613f0587828801613da1565b949793965093946060013593505050565b600081518084526020808501945080840160005b83811015613f4657815187529582019590820190600101613f2a565b509495945050505050565b6020815260006127676020830184613f16565b600060208284031215613f7657600080fd5b5051919050565b6001600160a01b0383168152604060208201526000610aa26040830184613f16565b60008060408385031215613fb257600080fd5b505080516020909101519092909150565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820161400157614001613fd9565b5060010190565b808201828112600083128015821682158216171561402857614028613fd9565b505092915050565b8082018082111561068a5761068a613fd9565b60006020828403121561405557600080fd5b8151801515811461276757600080fd5b808202811582820484141761068a5761068a613fd9565b634e487b7160e01b600052601260045260246000fd5b6000826140af57634e487b7160e01b600052601260045260246000fd5b500490565b60006101206001600160a01b03808d168452808c16602085015261ffff8b1660408501528960608501528860808501528160a08501526140f682850189613f16565b915083820360c085015261410a8288613f16565b951660e084015250506101000152979650505050505050565b60008060006060848603121561413857600080fd5b8351925060208401519150604084015190509250925092565b6020815261416b6020820183516001600160a01b03169052565b6000602083015161418760408401826001600160a01b03169052565b506040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160e083015260e08301516101008181850152808501519150506101208181850152808501519150506101c061014081818601526141f66101e0860184613f16565b9250808601519050601f196101608187860301818801526142178584613f16565b9450808801519250506101808187860301818801526142368584613f16565b945080880151925050506101a0614257818701836001600160a01b03169052565b959095015193019290925250919050565b600082601f83011261427957600080fd5b81516020614289613dc283613d7d565b82815260059290921b840181019181810190868411156142a857600080fd5b8286015b84811015613e0157805183529183019183016142ac565b600080604083850312156142d657600080fd5b825167ffffffffffffffff808211156142ee57600080fd5b6142fa86838701614268565b9350602085015191508082111561431057600080fd5b5061431d85828601614268565b9150509250929050565b60005b8381101561434257818101518382015260200161432a565b50506000910152565b6000825161435d818460208701614327565b9190910192915050565b86815260006020878184015260c0604084015261438760c0840188613f16565b838103606085015286518082528288019183019060005b818110156143c35783516001600160a01b03168352928401929184019160010161439e565b50506001600160a01b0396909616608085015250505060a00152949350505050565b8181038181111561068a5761068a613fd9565b634e487b7160e01b600052603160045260246000fd5b602081526000825180602084015261442d816040850160208701614327565b601f01601f1916919091016040019291505056fea2646970667358221220889a190e0d4b31b921d7ccda845e04e03fbfca2130b521e421917d8b394d19c864736f6c63430008110033000000000000000000000000e3ffc583dc176575eea7fd9df2a7c65f7e23f4c3000000000000000000000000be15bc0a3e37f1a3445defb4f0ff6eba0e4f19e20000000000000000000000001f85459ad40e25343929f958d4d44ed914b730e10000000000000000000000000000000000000000000000000000000000000080000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c70000000000000000000000009702230a8ea53601f5cd2dc00fdbc13d4df4a8c7000000000000000000000000df3e481a05f58c387af16867e9f5db7f931113c9000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000013ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffafffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a00000000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000e3ffc583dc176575eea7fd9df2a7c65f7e23f4c3000000000000000000000000be15bc0a3e37f1a3445defb4f0ff6eba0e4f19e20000000000000000000000001f85459ad40e25343929f958d4d44ed914b730e10000000000000000000000000000000000000000000000000000000000000080000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c70000000000000000000000009702230a8ea53601f5cd2dc00fdbc13d4df4a8c7000000000000000000000000df3e481a05f58c387af16867e9f5db7f931113c9000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000013ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffafffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a00000000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _joeRouter (address): 0xe3ffc583dc176575eea7fd9df2a7c65f7e23f4c3
Arg [1] : _keeper (address): 0xbe15bc0a3e37f1a3445defb4f0ff6eba0e4f19e2
Arg [2] : _vault (address): 0x1f85459ad40e25343929f958d4d44ed914b730e1
Arg [3] : _strategyParams (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
72 Constructor Arguments found :
Arg [0] : 000000000000000000000000e3ffc583dc176575eea7fd9df2a7c65f7e23f4c3
Arg [1] : 000000000000000000000000be15bc0a3e37f1a3445defb4f0ff6eba0e4f19e2
Arg [2] : 0000000000000000000000001f85459ad40e25343929f958d4d44ed914b730e1
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [4] : 000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7
Arg [5] : 0000000000000000000000009702230a8ea53601f5cd2dc00fdbc13d4df4a8c7
Arg [6] : 000000000000000000000000df3e481a05f58c387af16867e9f5db7f931113c9
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000014
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000380
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000600
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000013
Arg [13] : ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee
Arg [14] : fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0
Arg [15] : fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2
Arg [16] : fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4
Arg [17] : fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6
Arg [18] : fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8
Arg [19] : fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa
Arg [20] : fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc
Arg [21] : fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe
Arg [22] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [23] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [25] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [26] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [27] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [28] : 000000000000000000000000000000000000000000000000000000000000000c
Arg [29] : 000000000000000000000000000000000000000000000000000000000000000e
Arg [30] : 0000000000000000000000000000000000000000000000000000000000000010
Arg [31] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [32] : 0000000000000000000000000000000000000000000000000000000000000013
Arg [33] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [34] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [35] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [36] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [37] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [38] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [39] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [40] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [41] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [42] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [43] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [44] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [45] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [46] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [47] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [48] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [49] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [50] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [51] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [52] : 0000000000000000000000000000000000000000000000000000000000000013
Arg [53] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [54] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [55] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [56] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [57] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [58] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [59] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [60] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [61] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [62] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [63] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [64] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [65] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [66] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [67] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [68] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [69] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [70] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [71] : 0000000000000000000000000000000000000000000000000000000000000000
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.