Contract 0x5Da32F913D02d6D119359FAc5EbA9b2D68b3CFa4

Txn Hash Method
Block
From
To
Value [Txn Fee]
0xd0e1c53a35f5c4c7f6700ccd7a1d5be5ace3610acced7f3befca72158f60be34Harvest276505962023-03-19 18:08:151 day 10 hrs ago0xbe15bc0a3e37f1a3445defb4f0ff6eba0e4f19e2 IN  0x5da32f913d02d6d119359fac5eba9b2d68b3cfa40 AVAX0.02737668535
0x0aef9bacfd1438ee8c8efd7ad129d2c57d2079c11afb460579926c4af7c2e5caExecute Rebalanc...276211262023-03-19 1:38:162 days 3 hrs ago0xbe15bc0a3e37f1a3445defb4f0ff6eba0e4f19e2 IN  0x5da32f913d02d6d119359fac5eba9b2d68b3cfa40 AVAX0.1107260735
0x22a8cb7a291398598259f8ef2e3797f30267c7b37290714a103fcf2a803f14afHarvest276210552023-03-19 1:35:532 days 3 hrs ago0xbe15bc0a3e37f1a3445defb4f0ff6eba0e4f19e2 IN  0x5da32f913d02d6d119359fac5eba9b2d68b3cfa40 AVAX0.02408311535
0x4a8caa0fa8eb2b77e54497b1a735538d0dbbc58b05a9672ca9c1443bf9fd729fHarvest276210292023-03-19 1:35:002 days 3 hrs ago0xbe15bc0a3e37f1a3445defb4f0ff6eba0e4f19e2 IN  0x5da32f913d02d6d119359fac5eba9b2d68b3cfa40 AVAX0.02659268535
0x0a4ebe8fb63dff899bee149c5830668ade9742df6e7f2e05e4abc22ed41a178aExecute Rebalanc...276190662023-03-19 0:29:292 days 4 hrs ago0xbe15bc0a3e37f1a3445defb4f0ff6eba0e4f19e2 IN  0x5da32f913d02d6d119359fac5eba9b2d68b3cfa40 AVAX0.11073835535
0x8eb0c1b3f5f36d1fe72c1510b8cdcf7a4cffec68bc8ff261819ee9503a9a8b02Harvest276189432023-03-19 0:25:242 days 4 hrs ago0xbe15bc0a3e37f1a3445defb4f0ff6eba0e4f19e2 IN  0x5da32f913d02d6d119359fac5eba9b2d68b3cfa40 AVAX0.0038508435
0x09a4ed53750c00470491f6d12e15dc1b014425dad40aa81bbc51b87eecd70a9bSet Keeper276170332023-03-18 23:21:252 days 5 hrs ago0xbe15bc0a3e37f1a3445defb4f0ff6eba0e4f19e2 IN  0x5da32f913d02d6d119359fac5eba9b2d68b3cfa40 AVAX0.0010543435
0x5191a0e7b8aef8c7d88e1a13312d1981e21bdbb3d4767c6c640ff919895aafd90x61014060276165362023-03-18 23:04:512 days 5 hrs ago0xbe15bc0a3e37f1a3445defb4f0ff6eba0e4f19e2 IN  Create: StrategyTJLiquidityBookLB0 AVAX0.17852355535
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
StrategyTJLiquidityBookLB

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 34 : Ownable.sol
// 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);
    }
}

File 2 of 34 : Pausable.sol
// 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());
    }
}

File 3 of 34 : ERC20.sol
// 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 {}
}

File 4 of 34 : IERC20.sol
// 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);
}

File 5 of 34 : IERC20Metadata.sol
// 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);
}

File 6 of 34 : draft-IERC20Permit.sol
// 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);
}

File 7 of 34 : SafeERC20.sol
// 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");
        }
    }
}

File 8 of 34 : Address.sol
// 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);
        }
    }
}

File 9 of 34 : Context.sol
// 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;
    }
}

File 10 of 34 : IERC165.sol
// 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);
}

File 11 of 34 : EnumerableSet.sol
// 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;
    }
}

File 12 of 34 : JoeV2PeripheryErrors.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../LB/LBErrors.sol";

/** LiquidityAmounts Errors */

error LiquidityAmounts__LengthMismatch();

File 13 of 34 : LiquidityAmounts.sol
// 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);
        }
    }
}

File 14 of 34 : LBErrors.sol
// 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();

File 15 of 34 : IJoeFactory.sol
// 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;
}

File 16 of 34 : ILBFactory.sol
// 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;
    }
}

File 17 of 34 : ILBFlashLoanCallback.sol
// 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);
}

File 18 of 34 : ILBPair.sol
// 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;
}

File 19 of 34 : ILBRouter.sol
// 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;
}

File 20 of 34 : ILBToken.sol
// 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;
}

File 21 of 34 : IPendingOwnable.sol
// 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;
}

File 22 of 34 : IWAVAX.sol
// 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;
}

File 23 of 34 : BinHelper.sol
// 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;
        }
    }
}

File 24 of 34 : BitMath.sol
// 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;
        }
    }
}

File 25 of 34 : Constants.sol
// 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");
}

File 26 of 34 : FeeHelper.sol
// 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);
        }
    }
}

File 27 of 34 : Math128x128.sol
// 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;
    }
}

File 28 of 34 : Math512Bits.sol
// 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;
            }
        }
    }
}

File 29 of 34 : SafeCast.sol
// 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);
    }
}

File 30 of 34 : SafeMath.sol
// 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;
        }
    }
}

File 31 of 34 : StratManager.sol
// 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 {}
}

File 32 of 34 : ILBStrategy.sol
// 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);
}

File 33 of 34 : IYakRouter.sol
// 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;
}

File 34 of 34 : LBStrategy.sol
// 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;
    }
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract 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"}]

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


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.