ETH Price: $2,703.36 (+0.32%)

Token

Jones GLP (jGLP)

Overview

Max Total Supply

788,023.219500860765347566 jGLP

Holders

581 (0.00%)

Market

Price

$2.85 @ 0.001054 ETH

Onchain Market Cap

$2,245,866.18

Circulating Supply Market Cap

$0.00

Other Info

Token Contract (WITH 18 Decimals)

Balance
0.053673387087473212 jGLP

Value
$0.15 ( ~5.54864258826881E-05 ETH) [0.0000%]
0x71e7d05be74ff748c45402c06a941c822d756dc5
Loading...
Loading
Loading...
Loading
Loading...
Loading

OVERVIEW

Jones DAO is a yield, strategy, and liquidity protocol with vaults that enable 1-click access to institutional-grade strategies. These strategies unlock liquidity and capital efficiency for DeFi through yield-bearing tokens.

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xe6699853...4fc8458E0
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
JonesGlpCompoundRewards

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 51 : JonesGlpCompoundRewards.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 Jones DAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {ReentrancyGuard} from "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";
import {Governable} from "src/common/Governable.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol";
import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {OperableKeepable} from "src/common/OperableKeepable.sol";
import {IGmxRewardRouter} from "src/interfaces/IGmxRewardRouter.sol";
import {JonesGlpVaultRouter} from "src/glp/JonesGlpVaultRouter.sol";
import {IJonesGlpCompoundRewards} from "src/interfaces/IJonesGlpCompoundRewards.sol";
import {IJonesGlpRewardTracker} from "src/interfaces/IJonesGlpRewardTracker.sol";
import {IIncentiveReceiver} from "src/interfaces/IIncentiveReceiver.sol";
import {GlpJonesRewards} from "src/glp/rewards/GlpJonesRewards.sol";

contract JonesGlpCompoundRewards is IJonesGlpCompoundRewards, ERC20, OperableKeepable, ReentrancyGuard {
    using Math for uint256;

    uint256 public constant BASIS_POINTS = 1e12;

    address public constant weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
    address public constant usdc = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8;
    address public constant glp = 0x5402B5F40310bDED796c7D0F3FF6683f5C0cFfdf;
    IGmxRewardRouter public gmxRouter = IGmxRewardRouter(0xB95DB5B167D75e6d04227CfFFA61069348d271F5);

    IERC20 public asset;
    IERC20Metadata public vaultToken;

    uint256 public stableRetentionPercentage;
    uint256 public glpRetentionPercentage;

    uint256 public totalAssets; // total assets;
    uint256 public totalAssetsDeposits; // total assets deposits;

    mapping(address => uint256) public receiptBalance; // assets deposits

    JonesGlpVaultRouter public router;
    IJonesGlpRewardTracker public tracker;
    IIncentiveReceiver public incentiveReceiver;
    GlpJonesRewards public jonesRewards;

    constructor(
        uint256 _stableRetentionPercentage,
        uint256 _glpRetentionPercentage,
        IIncentiveReceiver _incentiveReceiver,
        IJonesGlpRewardTracker _tracker,
        GlpJonesRewards _jonesRewards,
        IERC20 _asset,
        IERC20Metadata _vaultToken,
        string memory _name,
        string memory _symbol
    ) Governable(msg.sender) ERC20(_name, _symbol) ReentrancyGuard() {
        if (_stableRetentionPercentage > BASIS_POINTS) {
            revert RetentionPercentageOutOfRange();
        }
        if (_glpRetentionPercentage > BASIS_POINTS) {
            revert RetentionPercentageOutOfRange();
        }

        stableRetentionPercentage = _stableRetentionPercentage;
        glpRetentionPercentage = _glpRetentionPercentage;
        incentiveReceiver = _incentiveReceiver;
        jonesRewards = _jonesRewards;

        asset = _asset;
        vaultToken = _vaultToken;

        tracker = _tracker;
    }

    // ============================= Keeper Functions ================================ //

    /**
     * @inheritdoc IJonesGlpCompoundRewards
     */
    function compound() external onlyOperatorOrKeeper {
        _compound();
    }

    // ============================= Operable Functions ================================ //

    /**
     * @inheritdoc IJonesGlpCompoundRewards
     */
    function deposit(uint256 _assets, address _receiver) external nonReentrant onlyOperator returns (uint256) {
        uint256 shares = previewDeposit(_assets);
        _deposit(_receiver, _assets, shares);

        return shares;
    }

    /**
     * @inheritdoc IJonesGlpCompoundRewards
     */
    function redeem(uint256 _shares, address _receiver) external nonReentrant onlyOperator returns (uint256) {
        uint256 assets = previewRedeem(_shares);
        _withdraw(_receiver, assets, _shares);

        return assets;
    }

    // ============================= Public Functions ================================ //

    /**
     * @inheritdoc IJonesGlpCompoundRewards
     */
    function previewDeposit(uint256 assets) public view returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Down);
    }

    /**
     * @inheritdoc IJonesGlpCompoundRewards
     */
    function previewRedeem(uint256 shares) public view returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Down);
    }

    /**
     * @inheritdoc IJonesGlpCompoundRewards
     */
    function totalAssetsToDeposits(address recipient, uint256 assets) public view returns (uint256) {
        uint256 totalRecipientAssets = _convertToAssets(balanceOf(recipient), Math.Rounding.Down);
        return assets.mulDiv(receiptBalance[recipient], totalRecipientAssets, Math.Rounding.Down);
    }

    // ============================= Governor Functions ================================ //

    /**
     * @notice Transfer all Glp managed by this contract to an address
     * @param _to Address to transfer funds
     */
    function emergencyGlpWithdraw(address _to) external onlyGovernor {
        _compound();
        router.redeemGlp(tracker.stakedAmount(address(this)), false);
        asset.transfer(_to, asset.balanceOf(address(this)));
    }

    /**
     * @notice Transfer all Stable assets managed by this contract to an address
     * @param _to Address to transfer funds
     */
    function emergencyStableWithdraw(address _to) external onlyGovernor {
        _compound();
        router.stableWithdrawalSignal(tracker.stakedAmount(address(this)), false);
        asset.transfer(_to, asset.balanceOf(address(this)));
    }

    /**
     * @notice Set new router contract
     * @param _router New router contract
     */
    function setRouter(JonesGlpVaultRouter _router) external onlyGovernor {
        router = _router;
    }

    /**
     * @notice Set new retention received
     * @param _incentiveReceiver New retention received
     */
    function setIncentiveReceiver(IIncentiveReceiver _incentiveReceiver) external onlyGovernor {
        incentiveReceiver = _incentiveReceiver;
    }

    /**
     * @notice Set new reward tracker contract
     * @param _tracker New reward tracker contract
     */
    function setRewardTracker(IJonesGlpRewardTracker _tracker) external onlyGovernor {
        tracker = _tracker;
    }

    /**
     * @notice Set new asset
     * @param _asset New asset
     */
    function setAsset(IERC20Metadata _asset) external onlyGovernor {
        asset = _asset;
    }

    /**
     * @notice Set new vault token
     * @param _vaultToken New vault token contract
     */
    function setVaultToken(IERC20Metadata _vaultToken) external onlyGovernor {
        vaultToken = _vaultToken;
    }

    /**
     * @notice Set new gmx router contract
     * @param _gmxRouter New gmx router contract
     */
    function setGmxRouter(IGmxRewardRouter _gmxRouter) external onlyGovernor {
        gmxRouter = _gmxRouter;
    }

    /**
     * @notice Set new retentions
     * @param _stableRetentionPercentage New stable retention
     * @param _glpRetentionPercentage New glp retention
     */
    function setNewRetentions(uint256 _stableRetentionPercentage, uint256 _glpRetentionPercentage)
        external
        onlyGovernor
    {
        if (_stableRetentionPercentage > BASIS_POINTS) {
            revert RetentionPercentageOutOfRange();
        }
        if (_glpRetentionPercentage > BASIS_POINTS) {
            revert RetentionPercentageOutOfRange();
        }

        stableRetentionPercentage = _stableRetentionPercentage;
        glpRetentionPercentage = _glpRetentionPercentage;
    }

    /**
     * @notice Set Jones Rewards Contract
     * @param _jonesRewards Contract that manage Jones Rewards
     */
    function setJonesRewards(GlpJonesRewards _jonesRewards) external onlyGovernor {
        jonesRewards = _jonesRewards;
    }

    // ============================= Private Functions ================================ //

    function _deposit(address receiver, uint256 assets, uint256 shares) private {
        vaultToken.transferFrom(msg.sender, address(this), assets);

        receiptBalance[receiver] = receiptBalance[receiver] + assets;

        vaultToken.approve(address(tracker), assets);
        tracker.stake(address(this), assets);

        totalAssetsDeposits = totalAssetsDeposits + assets;
        totalAssets = tracker.stakedAmount(address(this));

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);
    }

    function _withdraw(address receiver, uint256 assets, uint256 shares) private {
        uint256 depositAssets = totalAssetsToDeposits(receiver, assets);

        _burn(receiver, shares);

        receiptBalance[receiver] = receiptBalance[receiver] - depositAssets;

        totalAssetsDeposits = totalAssetsDeposits - depositAssets;

        tracker.withdraw(address(this), assets);

        vaultToken.approve(address(tracker), assets);
        tracker.stake(receiver, assets);

        totalAssets = tracker.stakedAmount(address(this));

        emit Withdraw(msg.sender, receiver, assets, shares);
    }

    function _compound() private {
        (uint256 stableRewards, uint256 glpRewards,) = router.claimRewards();
        if (glpRewards > 0) {
            uint256 retention = _retention(glpRewards, glpRetentionPercentage);
            if (retention > 0) {
                IERC20(weth).approve(address(incentiveReceiver), retention);
                incentiveReceiver.deposit(weth, retention);
                glpRewards = glpRewards - retention;
            }

            IERC20(weth).approve(gmxRouter.glpManager(), glpRewards);
            uint256 glpAmount = gmxRouter.mintAndStakeGlp(weth, glpRewards, 0, 0);
            glpRewards = glpAmount;

            IERC20(glp).approve(address(router), glpRewards);
            router.depositGlp(glpRewards, address(this), false);
            totalAssets = tracker.stakedAmount(address(this));

            // Information needed to calculate compounding rewards per Vault
            emit Compound(glpRewards, totalAssets, retention);
        }
        if (stableRewards > 0) {
            uint256 retention = _retention(stableRewards, stableRetentionPercentage);
            if (retention > 0) {
                IERC20(usdc).approve(address(incentiveReceiver), retention);
                incentiveReceiver.deposit(usdc, retention);
                stableRewards = stableRewards - retention;
            }

            IERC20(usdc).approve(address(router), stableRewards);
            router.depositStable(stableRewards, false, address(this));
            totalAssets = tracker.stakedAmount(address(this));

            // Information needed to calculate compounding rewards per Vault
            emit Compound(stableRewards, totalAssets, retention);
        }
    }

    function _convertToShares(uint256 assets, Math.Rounding rounding) private view returns (uint256 shares) {
        uint256 supply = totalSupply();

        return (assets == 0 || supply == 0)
            ? assets.mulDiv(10 ** decimals(), 10 ** vaultToken.decimals(), rounding)
            : assets.mulDiv(supply, totalAssets, rounding);
    }

    function _convertToAssets(uint256 shares, Math.Rounding rounding) private view returns (uint256 assets) {
        uint256 supply = totalSupply();
        return (supply == 0)
            ? shares.mulDiv(10 ** vaultToken.decimals(), 10 ** decimals(), rounding)
            : shares.mulDiv(totalAssets, supply, rounding);
    }

    function _retention(uint256 _rewards, uint256 _retentionPercentage) private pure returns (uint256) {
        return (_rewards * _retentionPercentage) / BASIS_POINTS;
    }

    function internalTransfer(address from, address to, uint256 amount) private {
        uint256 assets = previewRedeem(amount);
        uint256 depositAssets = totalAssetsToDeposits(from, assets);
        receiptBalance[from] = receiptBalance[from] - depositAssets;
        receiptBalance[to] = receiptBalance[to] + depositAssets;
        if (address(asset) == usdc) {
            jonesRewards.getReward(from);
            jonesRewards.withdraw(from, depositAssets);
            jonesRewards.stake(to, depositAssets);
        }
    }

    /// ============================= ERC20 Functions ================================ //

    function name() public view override returns (string memory) {
        return super.name();
    }

    function symbol() public view override returns (string memory) {
        return super.symbol();
    }

    function decimals() public view override returns (uint8) {
        return super.decimals();
    }

    function totalSupply() public view override returns (uint256) {
        return super.totalSupply();
    }

    function balanceOf(address account) public view override returns (uint256) {
        return super.balanceOf(account);
    }

    function transfer(address to, uint256 amount) public override returns (bool) {
        internalTransfer(msg.sender, to, amount);
        return super.transfer(to, amount);
    }

    function allowance(address owner, address spender) public view override returns (uint256) {
        return super.allowance(owner, spender);
    }

    function approve(address spender, uint256 amount) public override returns (bool) {
        return super.approve(spender, amount);
    }

    function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
        internalTransfer(from, to, amount);
        return super.transferFrom(from, to, amount);
    }
}

File 2 of 51 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 3 of 51 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 4 of 51 : 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 5 of 51 : IERC4626.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 *
 * _Available since v4.7._
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);
}

File 6 of 51 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 7 of 51 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 8 of 51 : 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 9 of 51 : ERC4626.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/extensions/ERC4626.sol)

pragma solidity ^0.8.0;

import "../ERC20.sol";
import "../utils/SafeERC20.sol";
import "../../../interfaces/IERC4626.sol";
import "../../../utils/math/Math.sol";

/**
 * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
 *
 * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
 * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
 * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
 * contract and not the "assets" token which is an independent contract.
 *
 * CAUTION: Deposits and withdrawals may incur unexpected slippage. Users should verify that the amount received of
 * shares or assets is as expected. EOAs should operate through a wrapper that performs these checks such as
 * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
 *
 * _Available since v4.7._
 */
abstract contract ERC4626 is ERC20, IERC4626 {
    using Math for uint256;

    IERC20Metadata private immutable _asset;

    /**
     * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
     */
    constructor(IERC20Metadata asset_) {
        _asset = asset_;
    }

    /** @dev See {IERC4626-asset}. */
    function asset() public view virtual override returns (address) {
        return address(_asset);
    }

    /** @dev See {IERC4626-totalAssets}. */
    function totalAssets() public view virtual override returns (uint256) {
        return _asset.balanceOf(address(this));
    }

    /** @dev See {IERC4626-convertToShares}. */
    function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {
        return _convertToShares(assets, Math.Rounding.Down);
    }

    /** @dev See {IERC4626-convertToAssets}. */
    function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {
        return _convertToAssets(shares, Math.Rounding.Down);
    }

    /** @dev See {IERC4626-maxDeposit}. */
    function maxDeposit(address) public view virtual override returns (uint256) {
        return _isVaultCollateralized() ? type(uint256).max : 0;
    }

    /** @dev See {IERC4626-maxMint}. */
    function maxMint(address) public view virtual override returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxWithdraw}. */
    function maxWithdraw(address owner) public view virtual override returns (uint256) {
        return _convertToAssets(balanceOf(owner), Math.Rounding.Down);
    }

    /** @dev See {IERC4626-maxRedeem}. */
    function maxRedeem(address owner) public view virtual override returns (uint256) {
        return balanceOf(owner);
    }

    /** @dev See {IERC4626-previewDeposit}. */
    function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Down);
    }

    /** @dev See {IERC4626-previewMint}. */
    function previewMint(uint256 shares) public view virtual override returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Up);
    }

    /** @dev See {IERC4626-previewWithdraw}. */
    function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Up);
    }

    /** @dev See {IERC4626-previewRedeem}. */
    function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Down);
    }

    /** @dev See {IERC4626-deposit}. */
    function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {
        require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max");

        uint256 shares = previewDeposit(assets);
        _deposit(_msgSender(), receiver, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-mint}. */
    function mint(uint256 shares, address receiver) public virtual override returns (uint256) {
        require(shares <= maxMint(receiver), "ERC4626: mint more than max");

        uint256 assets = previewMint(shares);
        _deposit(_msgSender(), receiver, assets, shares);

        return assets;
    }

    /** @dev See {IERC4626-withdraw}. */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual override returns (uint256) {
        require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max");

        uint256 shares = previewWithdraw(assets);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-redeem}. */
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual override returns (uint256) {
        require(shares <= maxRedeem(owner), "ERC4626: redeem more than max");

        uint256 assets = previewRedeem(shares);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return assets;
    }

    /**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     *
     * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset
     * would represent an infinite amout of shares.
     */
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) {
        uint256 supply = totalSupply();
        return
            (assets == 0 || supply == 0)
                ? assets.mulDiv(10**decimals(), 10**_asset.decimals(), rounding)
                : assets.mulDiv(supply, totalAssets(), rounding);
    }

    /**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */
    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) {
        uint256 supply = totalSupply();
        return
            (supply == 0)
                ? shares.mulDiv(10**_asset.decimals(), 10**decimals(), rounding)
                : shares.mulDiv(totalAssets(), supply, rounding);
    }

    /**
     * @dev Deposit/mint common workflow.
     */
    function _deposit(
        address caller,
        address receiver,
        uint256 assets,
        uint256 shares
    ) internal virtual {
        // If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the
        // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
        // assets are transfered and before the shares are minted, which is a valid state.
        // slither-disable-next-line reentrancy-no-eth
        SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);
        _mint(receiver, shares);

        emit Deposit(caller, receiver, assets, shares);
    }

    /**
     * @dev Withdraw/redeem common workflow.
     */
    function _withdraw(
        address caller,
        address receiver,
        address owner,
        uint256 assets,
        uint256 shares
    ) internal virtual {
        if (caller != owner) {
            _spendAllowance(owner, caller, shares);
        }

        // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
        // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
        // shares are burned and after the assets are transfered, which is a valid state.
        _burn(owner, shares);
        SafeERC20.safeTransfer(_asset, receiver, assets);

        emit Withdraw(caller, receiver, owner, assets, shares);
    }

    function _isVaultCollateralized() private view returns (bool) {
        return totalAssets() > 0 || totalSupply() == 0;
    }
}

File 10 of 51 : 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 11 of 51 : 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 12 of 51 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 13 of 51 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 14 of 51 : 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 15 of 51 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 16 of 51 : MerkleProof.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The proofs can be generated using the JavaScript library
 * https://github.com/miguelmota/merkletreejs[merkletreejs].
 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
 *
 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     *
     * _Available since v4.7._
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     *
     * _Available since v4.7._
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be proved to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * _Available since v4.7._
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * _Available since v4.7._
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and the sibling nodes in `proof`,
     * consuming from one or the other at each step according to the instructions given by
     * `proofFlags`.
     *
     * _Available since v4.7._
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}
     *
     * _Available since v4.7._
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

File 17 of 51 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 18 of 51 : 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 19 of 51 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 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.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // 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.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // 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;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb computation, we are able to compute `result = 2**(k/2)` which is a
        // good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 1;
        }

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        uint256 result = sqrt(a);
        if (rounding == Rounding.Up && result * result < a) {
            result += 1;
        }
        return result;
    }
}

File 20 of 51 : GlpAdapter.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {IERC20, IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC4626} from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol";
import {IGmxRewardRouter} from "../interfaces/IGmxRewardRouter.sol";
import {IGlpManager, IGMXVault} from "../interfaces/IGlpManager.sol";
import {IJonesGlpVaultRouter} from "../interfaces/IJonesGlpVaultRouter.sol";
import {Operable, Governable} from "../common/Operable.sol";
import {ReentrancyGuard} from "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";
import {MerkleProof} from "openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol";
import {WhitelistController} from "src/common/WhitelistController.sol";
import {IAggregatorV3} from "src/interfaces/IAggregatorV3.sol";
import {JonesGlpLeverageStrategy} from "src/glp/strategies/JonesGlpLeverageStrategy.sol";
import {JonesGlpStableVault} from "src/glp/vaults/JonesGlpStableVault.sol";

contract GlpAdapter is Operable, ReentrancyGuard {
    IJonesGlpVaultRouter public vaultRouter;
    IGmxRewardRouter public gmxRouter = IGmxRewardRouter(0xB95DB5B167D75e6d04227CfFFA61069348d271F5);
    IAggregatorV3 public oracle = IAggregatorV3(0x50834F3163758fcC1Df9973b6e91f0F0F0434aD3);
    IERC20 public glp = IERC20(0x5402B5F40310bDED796c7D0F3FF6683f5C0cFfdf);
    IERC20 public usdc = IERC20(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8);
    WhitelistController public controller;
    JonesGlpLeverageStrategy public strategy;
    JonesGlpStableVault public stableVault;

    bytes32 private root;
    uint256 public flexibleTotalCap;
    bool public hatlistStatus;
    bool public useFlexibleCap;

    mapping(address => bool) public isValid;

    uint256 public constant BASIS_POINTS = 1e12;

    constructor(address[] memory _tokens, address _controller, address _strategy, address _stableVault)
        Governable(msg.sender)
    {
        uint8 i = 0;
        for (; i < _tokens.length;) {
            _editToken(_tokens[i], true);
            unchecked {
                i++;
            }
        }

        controller = WhitelistController(_controller);
        strategy = JonesGlpLeverageStrategy(_strategy);
        stableVault = JonesGlpStableVault(_stableVault);
    }

    function zapToGlp(address _token, uint256 _amount, bool _compound, bytes32[] memory _proof)
        external
        nonReentrant
        validToken(_token)
        returns (uint256)
    {
        _validateSender(_proof);

        IERC20(_token).transferFrom(msg.sender, address(this), _amount);

        IERC20(_token).approve(gmxRouter.glpManager(), _amount);
        uint256 mintedGlp = gmxRouter.mintAndStakeGlp(_token, _amount, 0, 0);

        glp.approve(address(vaultRouter), mintedGlp);
        uint256 receipts = vaultRouter.depositGlp(mintedGlp, msg.sender, _compound);

        return receipts;
    }

    function zapToGlpEth(bool _compound, bytes32[] memory _proof) external payable nonReentrant returns (uint256) {
        _validateSender(_proof);

        uint256 mintedGlp = gmxRouter.mintAndStakeGlpETH{value: msg.value}(0, 0);

        glp.approve(address(vaultRouter), mintedGlp);

        uint256 receipts = vaultRouter.depositGlp(mintedGlp, msg.sender, _compound);

        return receipts;
    }

    function redeemGlpBasket(uint256 _shares, bool _compound, address _token, bool _native)
        external
        nonReentrant
        validToken(_token)
        returns (uint256)
    {
        _onlyEOA();

        uint256 assetsReceived = vaultRouter.redeemGlpAdapter(_shares, _compound, _token, msg.sender, _native);

        return assetsReceived;
    }

    function depositGlp(uint256 _assets, bool _compound, bytes32[] memory _proof)
        external
        nonReentrant
        returns (uint256)
    {
        _validateSender(_proof);

        glp.transferFrom(msg.sender, address(this), _assets);

        glp.approve(address(vaultRouter), _assets);

        uint256 receipts = vaultRouter.depositGlp(_assets, msg.sender, _compound);

        return receipts;
    }

    function depositStable(uint256 _assets, bool _compound, bytes32[] memory _proof)
        external
        nonReentrant
        returns (uint256)
    {
        _validateSender(_proof);

        if (useFlexibleCap) {
            _checkUsdcCap(_assets);
        }

        usdc.transferFrom(msg.sender, address(this), _assets);

        usdc.approve(address(vaultRouter), _assets);

        uint256 receipts = vaultRouter.depositStable(_assets, _compound, msg.sender);

        return receipts;
    }

    function updateGmxRouter(address _gmxRouter) external onlyGovernor {
        gmxRouter = IGmxRewardRouter(_gmxRouter);
    }

    function updateVaultRouter(address _vaultRouter) external onlyGovernor {
        vaultRouter = IJonesGlpVaultRouter(_vaultRouter);
    }

    function _editToken(address _token, bool _valid) internal {
        isValid[_token] = _valid;
    }

    function updateRoot(bytes32 _root) external onlyGovernor {
        root = _root;
    }

    function toggleHatlist(bool _status) external onlyGovernor {
        hatlistStatus = _status;
    }

    function toggleFlexibleCap(bool _status) external onlyGovernor {
        useFlexibleCap = _status;
    }

    function updateFlexibleCap(uint256 _newAmount) public onlyGovernor {
        //18 decimals -> $1mi = 1_000_000e18
        flexibleTotalCap = _newAmount;
    }

    function getFlexibleCap() public view returns (uint256) {
        return flexibleTotalCap; //18 decimals
    }

    function usingFlexibleCap() public view returns (bool) {
        return useFlexibleCap;
    }

    function usingHatlist() public view returns (bool) {
        return hatlistStatus;
    }

    function getUsdcCap() public view returns (uint256 usdcCap) {
        usdcCap = (flexibleTotalCap * (strategy.getTargetLeverage() - BASIS_POINTS)) / strategy.getTargetLeverage();
    }

    function belowCap(uint256 _amount) public view returns (bool) {
        uint256 increaseDecimals = 10;
        (, int256 lastPrice,,,) = oracle.latestRoundData(); //8 decimals
        uint256 price = uint256(lastPrice) * (10 ** increaseDecimals); //18 DECIMALS
        uint256 usdcCap = getUsdcCap(); //18 decimals
        uint256 stableTvl = stableVault.tvl(); //18 decimals
        uint256 denominator = 1e6;

        uint256 notional = (price * _amount) / denominator;

        if (stableTvl + notional > usdcCap) {
            return false;
        }

        return true;
    }

    function _onlyEOA() private view {
        if (msg.sender != tx.origin && !controller.isWhitelistedContract(msg.sender)) {
            revert NotWhitelisted();
        }
    }

    function _isHatlisted(bytes32[] memory _proof) private view {
        bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(msg.sender))));
        bool verified = MerkleProof.verify(_proof, root, leaf);

        if (!verified) {
            revert NotHatlisted();
        }
    }

    function _validateSender(bytes32[] memory _proof) private view {
        if (hatlistStatus) {
            _isHatlisted(_proof);
            _onlyEOA();
        } else {
            _onlyEOA();
        }
    }

    function _checkUsdcCap(uint256 _amount) private view {
        if (!belowCap(_amount)) {
            revert OverUsdcCap();
        }
    }

    function editToken(address _token, bool _valid) external onlyGovernor {
        _editToken(_token, _valid);
    }

    modifier validToken(address _token) {
        require(isValid[_token], "Invalid token.");
        _;
    }

    error NotHatlisted();
    error OverUsdcCap();
    error NotWhitelisted();
}

File 21 of 51 : Governable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol";

abstract contract Governable is AccessControl {
    bytes32 public constant GOVERNOR = bytes32("GOVERNOR");

    constructor(address _governor) {
        _grantRole(GOVERNOR, _governor);
    }

    modifier onlyGovernor() {
        _onlyGovernor();
        _;
    }

    function updateGovernor(address _newGovernor) external onlyGovernor {
        _revokeRole(GOVERNOR, msg.sender);
        _grantRole(GOVERNOR, _newGovernor);

        emit GovernorUpdated(msg.sender, _newGovernor);
    }

    function _onlyGovernor() private view {
        if (!hasRole(GOVERNOR, msg.sender)) {
            revert CallerIsNotGovernor();
        }
    }

    event GovernorUpdated(address _oldGovernor, address _newGovernor);

    error CallerIsNotGovernor();
}

File 22 of 51 : Operable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol";
import {Governable} from "./Governable.sol";

abstract contract Operable is Governable {
    bytes32 public constant OPERATOR = bytes32("OPERATOR");

    modifier onlyOperator() {
        if (!hasRole(OPERATOR, msg.sender)) {
            revert CallerIsNotOperator();
        }

        _;
    }

    function addOperator(address _newOperator) external onlyGovernor {
        _grantRole(OPERATOR, _newOperator);

        emit OperatorAdded(_newOperator);
    }

    function removeOperator(address _operator) external onlyGovernor {
        _revokeRole(OPERATOR, _operator);

        emit OperatorRemoved(_operator);
    }

    event OperatorAdded(address _newOperator);
    event OperatorRemoved(address _operator);

    error CallerIsNotOperator();
}

File 23 of 51 : OperableKeepable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {Governable} from "./Governable.sol";

abstract contract OperableKeepable is Governable {
    bytes32 public constant OPERATOR = bytes32("OPERATOR");
    bytes32 public constant KEEPER = bytes32("KEEPER");

    modifier onlyOperator() {
        if (!hasRole(OPERATOR, msg.sender)) {
            revert CallerIsNotOperator();
        }

        _;
    }

    modifier onlyKeeper() {
        if (!hasRole(KEEPER, msg.sender)) {
            revert CallerIsNotKeeper();
        }

        _;
    }

    modifier onlyOperatorOrKeeper() {
        if (!(hasRole(OPERATOR, msg.sender) || hasRole(KEEPER, msg.sender))) {
            revert CallerIsNotAllowed();
        }

        _;
    }

    modifier onlyGovernorOrKeeper() {
        if (!(hasRole(GOVERNOR, msg.sender) || hasRole(KEEPER, msg.sender))) {
            revert CallerIsNotAllowed();
        }

        _;
    }

    function addOperator(address _newOperator) external onlyGovernor {
        _grantRole(OPERATOR, _newOperator);

        emit OperatorAdded(_newOperator);
    }

    function removeOperator(address _operator) external onlyGovernor {
        _revokeRole(OPERATOR, _operator);

        emit OperatorRemoved(_operator);
    }

    function addKeeper(address _newKeeper) external onlyGovernor {
        _grantRole(KEEPER, _newKeeper);

        emit KeeperAdded(_newKeeper);
    }

    function removeKeeper(address _operator) external onlyGovernor {
        _revokeRole(KEEPER, _operator);

        emit KeeperRemoved(_operator);
    }

    event OperatorAdded(address _newOperator);
    event OperatorRemoved(address _operator);

    error CallerIsNotOperator();

    event KeeperAdded(address _newKeeper);
    event KeeperRemoved(address _operator);

    error CallerIsNotKeeper();

    error CallerIsNotAllowed();
}

File 24 of 51 : Pausable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

abstract contract Pausable {
    bool private _paused;
    bool private _emergencyPaused;

    constructor() {
        _paused = false;
        _emergencyPaused = false;
    }

    function paused() public view returns (bool) {
        return _paused;
    }

    function emergencyPaused() public view returns (bool) {
        return _emergencyPaused;
    }

    function _requireNotPaused() internal view {
        if (paused()) {
            revert ErrorPaused();
        }
    }

    function _requireNotEmergencyPaused() internal view {
        if (emergencyPaused()) {
            revert ErrorEmergencyPaused();
        }
    }

    function _pause() internal whenNotPaused {
        _paused = true;
        emit Paused(msg.sender);
    }

    function _unpause() internal whenPaused {
        _paused = false;
        emit Unpaused(msg.sender);
    }

    function _emergencyPause() internal whenNotEmergencyPaused {
        _paused = true;
        _emergencyPaused = true;
        emit EmergencyPaused(msg.sender);
    }

    function _emergencyUnpause() internal whenEmergencyPaused {
        _emergencyPaused = false;
        _paused = false;
        emit EmergencyUnpaused(msg.sender);
    }

    modifier whenPaused() {
        if (!paused()) {
            revert ErrorNotPaused();
        }
        _;
    }

    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    modifier whenEmergencyPaused() {
        if (!emergencyPaused()) {
            revert ErrorNotEmergencyPaused();
        }
        _;
    }

    modifier whenNotEmergencyPaused() {
        _requireNotEmergencyPaused();
        _;
    }

    event Paused(address _account);
    event Unpaused(address _account);
    event EmergencyPaused(address _account);
    event EmergencyUnpaused(address _account);

    error ErrorPaused();
    error ErrorEmergencyPaused();
    error ErrorNotPaused();
    error ErrorNotEmergencyPaused();
}

File 25 of 51 : WhitelistController.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol";
import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";
import {IWhitelistController} from "../interfaces/IWhitelistController.sol";

contract WhitelistController is IWhitelistController, AccessControl, Ownable {
    mapping(bytes32 => IWhitelistController.RoleInfo) public roleInfo;
    mapping(address => bytes32) public userInfo;
    mapping(bytes32 => bool) public roleExists;

    bytes32 private constant INTERNAL = bytes32("INTERNAL");
    bytes32 private constant WHITELISTED_CONTRACTS = bytes32("WHITELISTED_CONTRACTS");
    uint256 public constant BASIS_POINTS = 1e12;

    constructor() {
        IWhitelistController.RoleInfo memory DEFAULT_ROLE = IWhitelistController.RoleInfo(false, false, 3e10, 97e8);

        bytes32 defaultRole = bytes32(0);
        createRole(defaultRole, DEFAULT_ROLE);
    }

    function updateDefaultRole(uint256 _jglpRetention, uint256 _jusdcRetention) public onlyOwner {
        IWhitelistController.RoleInfo memory NEW_DEFAULT_ROLE =
            IWhitelistController.RoleInfo(false, false, _jglpRetention, _jusdcRetention);

        bytes32 defaultRole = bytes32(0);
        createRole(defaultRole, NEW_DEFAULT_ROLE);
    }

    function hasRole(bytes32 role, address account)
        public
        view
        override(IWhitelistController, AccessControl)
        returns (bool)
    {
        return super.hasRole(role, account);
    }

    function isInternalContract(address _account) public view returns (bool) {
        return hasRole(INTERNAL, _account);
    }

    function isWhitelistedContract(address _account) public view returns (bool) {
        return hasRole(WHITELISTED_CONTRACTS, _account);
    }

    function addToRole(bytes32 ROLE, address _account) public onlyOwner validRole(ROLE) {
        _addRoleUser(ROLE, _account);
    }

    function addToInternalContract(address _account) public onlyOwner {
        _grantRole(INTERNAL, _account);
    }

    function addToWhitelistContracts(address _account) public onlyOwner {
        _grantRole(WHITELISTED_CONTRACTS, _account);
    }

    function bulkAddToWhitelistContracts(address[] calldata _accounts) public onlyOwner {
        uint256 length = _accounts.length;
        for (uint8 i = 0; i < length;) {
            _grantRole(WHITELISTED_CONTRACTS, _accounts[i]);
            unchecked {
                ++i;
            }
        }
    }

    function createRole(bytes32 _roleName, IWhitelistController.RoleInfo memory _roleInfo) public onlyOwner {
        roleExists[_roleName] = true;
        roleInfo[_roleName] = _roleInfo;
    }

    function _addRoleUser(bytes32 _role, address _user) internal {
        userInfo[_user] = _role;
    }

    function getUserRole(address _user) public view returns (bytes32) {
        return userInfo[_user];
    }

    function getDefaultRole() public view returns (IWhitelistController.RoleInfo memory) {
        bytes32 defaultRole = bytes32(0);
        return getRoleInfo(defaultRole);
    }

    function getRoleInfo(bytes32 _role) public view returns (IWhitelistController.RoleInfo memory) {
        return roleInfo[_role];
    }

    function removeUserFromRole(address _user) public onlyOwner {
        bytes32 zeroRole = bytes32(0x0);
        userInfo[_user] = zeroRole;
    }

    function removeFromInternalContract(address _account) public onlyOwner {
        _revokeRole(INTERNAL, _account);
    }

    function removeFromWhitelistContract(address _account) public onlyOwner {
        _revokeRole(WHITELISTED_CONTRACTS, _account);
    }

    function bulkRemoveFromWhitelistContract(address[] calldata _accounts) public onlyOwner {
        uint256 length = _accounts.length;
        for (uint8 i = 0; i < length;) {
            _revokeRole(WHITELISTED_CONTRACTS, _accounts[i]);
            unchecked {
                ++i;
            }
        }
    }

    modifier validRole(bytes32 _role) {
        require(roleExists[_role], "Role does not exist!");
        _;
    }
}

File 26 of 51 : JonesGlpVaultRouter.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 Jones DAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IERC4626} from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol";
import {Pausable} from "../common/Pausable.sol";
import {WhitelistController} from "../common/WhitelistController.sol";
import {JonesGlpVault} from "./vaults/JonesGlpVault.sol";
import {JonesGlpStableVault} from "./vaults/JonesGlpStableVault.sol";
import {Governable} from "../common/Governable.sol";
import {GlpJonesRewards} from "./rewards/GlpJonesRewards.sol";
import {IGmxRewardRouter} from "../interfaces/IGmxRewardRouter.sol";
import {IWhitelistController} from "../interfaces/IWhitelistController.sol";
import {IJonesGlpLeverageStrategy} from "../interfaces/IJonesGlpLeverageStrategy.sol";
import {IIncentiveReceiver} from "../interfaces/IIncentiveReceiver.sol";
import {IJonesGlpRewardTracker} from "../interfaces/IJonesGlpRewardTracker.sol";
import {GlpAdapter} from "../adapters/GlpAdapter.sol";
import {IJonesGlpCompoundRewards} from "../interfaces/IJonesGlpCompoundRewards.sol";
import {ReentrancyGuard} from "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";
import {Errors} from "src/interfaces/Errors.sol";

contract JonesGlpVaultRouter is Governable, Pausable, ReentrancyGuard {
    bool public initialized;

    struct WithdrawalSignal {
        uint256 targetEpoch;
        uint256 commitedShares;
        bool redeemed;
        bool compound;
    }

    IGmxRewardRouter private constant router = IGmxRewardRouter(0xB95DB5B167D75e6d04227CfFFA61069348d271F5);
    address private constant weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;

    JonesGlpVault private glpVault;
    JonesGlpStableVault private glpStableVault;
    IJonesGlpLeverageStrategy public strategy;
    GlpJonesRewards private jonesRewards;
    IJonesGlpRewardTracker public glpRewardTracker;
    IJonesGlpRewardTracker public stableRewardTracker;
    IJonesGlpCompoundRewards private stableCompoundRewards;
    IJonesGlpCompoundRewards private glpCompoundRewards;
    IWhitelistController private whitelistController;
    IIncentiveReceiver private incentiveReceiver;
    GlpAdapter private adapter;

    IERC20 private glp;
    IERC20 private stable;

    // vault asset -> reward tracker
    mapping(address => IJonesGlpRewardTracker) public rewardTrackers;
    // vault asset -> reward compounder
    mapping(address => IJonesGlpCompoundRewards) public rewardCompounder;

    mapping(address => mapping(uint256 => WithdrawalSignal)) public userSignal;

    uint256 private constant BASIS_POINTS = 1e12;
    uint256 private constant EPOCH_DURATION = 1 days;
    uint256 public EXIT_COOLDOWN;
    uint256 public pendingWithdrawals;

    constructor(
        JonesGlpVault _glpVault,
        JonesGlpStableVault _glpStableVault,
        IJonesGlpLeverageStrategy _strategy,
        GlpJonesRewards _jonesRewards,
        IJonesGlpRewardTracker _glpRewardTracker,
        IJonesGlpRewardTracker _stableRewardTracker,
        IJonesGlpCompoundRewards _glpCompoundRewards,
        IJonesGlpCompoundRewards _stableCompoundRewards,
        IWhitelistController _whitelistController,
        IIncentiveReceiver _incentiveReceiver
    ) Governable(msg.sender) {
        glpVault = _glpVault;
        glpStableVault = _glpStableVault;
        strategy = _strategy;
        jonesRewards = _jonesRewards;
        glpRewardTracker = _glpRewardTracker;
        stableRewardTracker = _stableRewardTracker;
        glpCompoundRewards = _glpCompoundRewards;
        stableCompoundRewards = _stableCompoundRewards;
        whitelistController = _whitelistController;

        incentiveReceiver = _incentiveReceiver;
    }

    function initialize(address _glp, address _stable, address _adapter) external onlyGovernor {
        if (initialized) {
            revert Errors.AlreadyInitialized();
        }

        rewardTrackers[_glp] = glpRewardTracker;
        rewardTrackers[_stable] = stableRewardTracker;
        rewardCompounder[_glp] = glpCompoundRewards;
        rewardCompounder[_stable] = stableCompoundRewards;

        glp = IERC20(_glp);
        stable = IERC20(_stable);
        adapter = GlpAdapter(_adapter);
        initialized = true;
    }

    // ============================= Whitelisted functions ================================ //

    /**
     * @notice The Adapter contract can deposit GLP to the system on behalf of the _sender
     * @param _assets Amount of assets deposited
     * @param _sender address of who is deposit the assets
     * @param _compound optional compounding rewards
     * @return Amount of shares jGLP minted
     */
    function depositGlp(uint256 _assets, address _sender, bool _compound) external whenNotPaused returns (uint256) {
        _onlyInternalContract(); //can only be adapter or compounder

        bytes32 role = whitelistController.getUserRole(_sender);
        IWhitelistController.RoleInfo memory info = whitelistController.getRoleInfo(role);

        IJonesGlpLeverageStrategy _strategy = strategy;
        JonesGlpVault _glpVault = glpVault;

        uint256 assetsUsdValue = _strategy.getStableGlpValue(_assets);
        uint256 underlyingUsdValue = _strategy.getStableGlpValue(_strategy.getUnderlyingGlp());
        uint256 maxTvlGlp = getMaxCapGlp();

        if ((assetsUsdValue + underlyingUsdValue) * BASIS_POINTS > maxTvlGlp && !info.jGLP_BYPASS_CAP) {
            revert Errors.MaxGlpTvlReached();
        }

        if (_compound) {
            glpCompoundRewards.compound();
        }

        (uint256 compoundShares, uint256 vaultShares) = _deposit(_glpVault, _sender, _assets, _compound);

        _strategy.onGlpDeposit(_assets);

        if (_compound) {
            emit DepositGlp(_sender, _assets, compoundShares, _compound);
            return compoundShares;
        }

        emit DepositGlp(_sender, _assets, vaultShares, _compound);

        return vaultShares;
    }

    /**
     * @notice Users & Whitelist contract can redeem GLP from the system
     * @param _shares Amount of jGLP deposited to redeem GLP
     * @param _compound flag if the rewards are compounding
     * @return Amount of GLP remdeemed
     */
    function redeemGlp(uint256 _shares, bool _compound)
        external
        whenNotEmergencyPaused
        nonReentrant
        returns (uint256)
    {
        _onlyEOA();

        if (_compound) {
            glpCompoundRewards.compound();
            _shares = _unCompoundGlp(_shares, msg.sender);
        }

        glpRewardTracker.withdraw(msg.sender, _shares);
        JonesGlpVault _glpVault = glpVault;

        uint256 glpAmount = _glpVault.previewRedeem(_shares);

        _glpVault.burn(address(this), _shares);

        //We can't send glpAmount - retention here because it'd mess our rebalance
        glpAmount = strategy.onGlpRedeem(glpAmount);

        if (glpAmount > 0) {
            glpAmount = _distributeGlp(glpAmount, msg.sender, _compound);
        }

        return glpAmount;
    }

    /**
     * @notice User & Whitelist contract can redeem GLP using any asset of GLP basket from the system
     * @param _shares Amount of jGLP deposited to redeem GLP
     * @param _compound flag if the rewards are compounding
     * @param _token address of asset token
     * @param _user address of the user that will receive the assets
     * @param _native flag if the user will receive raw ETH
     * @return Amount of assets redeemed
     */
    function redeemGlpAdapter(uint256 _shares, bool _compound, address _token, address _user, bool _native)
        external
        whenNotEmergencyPaused
        nonReentrant
        returns (uint256)
    {
        if (msg.sender != address(adapter)) {
            revert Errors.OnlyAdapter();
        }

        if (_compound) {
            glpCompoundRewards.compound();
            _shares = _unCompoundGlp(_shares, _user);
        }
        glpRewardTracker.withdraw(_user, _shares);
        JonesGlpVault _glpVault = glpVault;

        uint256 glpAmount = _glpVault.previewRedeem(_shares);

        _glpVault.burn(address(this), _shares);

        //We can't send glpAmount - retention here because it'd mess our rebalance
        glpAmount = strategy.onGlpRedeem(glpAmount);

        if (glpAmount > 0) {
            glpAmount = _distributeGlpAdapter(glpAmount, _user, _token, _native, _compound);
        }

        return glpAmount;
    }

    /**
     * @notice adapter & compounder can deposit Stable assets to the system
     * @param _assets Amount of Stables deposited
     * @param _compound optional compounding rewards
     * @return Amount of shares jUSDC minted
     */
    function depositStable(uint256 _assets, bool _compound, address _user) external whenNotPaused returns (uint256) {
        _onlyInternalContract(); //can only be adapter or compounder

        if (_compound) {
            stableCompoundRewards.compound();
        }

        (uint256 shares, uint256 track) = _deposit(glpStableVault, _user, _assets, _compound);

        if (_user != address(rewardCompounder[address(stable)])) {
            jonesRewards.stake(_user, track);
        }

        strategy.onStableDeposit();

        emit DepositStables(_user, _assets, shares, _compound);

        return shares;
    }

    /**
     * @notice Users can signal a stable redeem or redeem directly if user has the role to do it.
     * @dev The Jones & Stable rewards stop here
     * @param _shares Amount of shares jUSDC to redeem
     * @param _compound flag if the rewards are compounding
     * @return Epoch when will be possible the redeem or the amount of stables received in case user has special role
     */
    function stableWithdrawalSignal(uint256 _shares, bool _compound)
        external
        whenNotEmergencyPaused
        returns (uint256)
    {
        _onlyEOA();

        bytes32 userRole = whitelistController.getUserRole(msg.sender);
        IWhitelistController.RoleInfo memory info = whitelistController.getRoleInfo(userRole);

        uint256 targetEpoch = currentEpoch() + EXIT_COOLDOWN;
        WithdrawalSignal storage userWithdrawalSignal = userSignal[msg.sender][targetEpoch];

        if (userWithdrawalSignal.commitedShares > 0) {
            revert Errors.WithdrawalSignalAlreadyDone();
        }

        if (_compound) {
            stableCompoundRewards.compound();
            uint256 assets = stableCompoundRewards.previewRedeem(_shares);
            uint256 assetDeposited = stableCompoundRewards.totalAssetsToDeposits(msg.sender, assets);
            jonesRewards.getReward(msg.sender);
            jonesRewards.withdraw(msg.sender, assetDeposited);
            _shares = _unCompoundStables(_shares);
        } else {
            jonesRewards.getReward(msg.sender);
            jonesRewards.withdraw(msg.sender, _shares);
        }

        rewardTrackers[address(stable)].withdraw(msg.sender, _shares);

        if (info.jUSDC_BYPASS_TIME) {
            return _redeemDirectly(_shares, info.jUSDC_RETENTION, _compound);
        }

        userWithdrawalSignal.targetEpoch = targetEpoch;
        userWithdrawalSignal.commitedShares = _shares;
        userWithdrawalSignal.compound = _compound;

        pendingWithdrawals = pendingWithdrawals + glpStableVault.previewRedeem(_shares);

        emit StableWithdrawalSignal(msg.sender, _shares, targetEpoch, _compound);

        return targetEpoch;
    }

    function _redeemDirectly(uint256 _shares, uint256 _retention, bool _compound) private returns (uint256) {
        uint256 stableAmount = glpStableVault.previewRedeem(_shares);
        uint256 stablesFromVault = _borrowStables(stableAmount);
        uint256 gmxIncentive;

        // Only redeem from strategy if there is not enough on the vault
        if (stablesFromVault < stableAmount) {
            uint256 difference = stableAmount - stablesFromVault;
            gmxIncentive = (difference * strategy.getRedeemStableGMXIncentive(difference) * 1e8) / BASIS_POINTS;
            strategy.onStableRedeem(difference, difference - gmxIncentive);
        }

        uint256 remainderStables = stableAmount - gmxIncentive;

        IERC20 stableToken = stable;

        if (stableToken.balanceOf(address(this)) < remainderStables) {
            revert Errors.NotEnoughStables();
        }

        glpStableVault.burn(address(this), _shares);

        uint256 retention = ((stableAmount * _retention) / BASIS_POINTS);

        uint256 realRetention = gmxIncentive < retention ? retention - gmxIncentive : 0;

        uint256 amountAfterRetention = remainderStables - realRetention;

        if (amountAfterRetention > 0) {
            stableToken.transfer(msg.sender, amountAfterRetention);
        }

        if (realRetention > 0) {
            stableToken.approve(address(stableRewardTracker), realRetention);
            stableRewardTracker.depositRewards(realRetention);
        }

        // Information needed to calculate stable retentions
        emit RedeemStable(msg.sender, amountAfterRetention, retention, realRetention, _compound);

        return amountAfterRetention;
    }

    /**
     * @notice Users can cancel the signal to stable redeem
     * @param _epoch Target epoch
     * @param _compound true if the rewards should be compound
     */
    function cancelStableWithdrawalSignal(uint256 _epoch, bool _compound) external {
        WithdrawalSignal memory userWithdrawalSignal = userSignal[msg.sender][_epoch];

        if (userWithdrawalSignal.redeemed) {
            revert Errors.WithdrawalAlreadyCompleted();
        }

        uint256 snapshotCommitedShares = userWithdrawalSignal.commitedShares;

        if (snapshotCommitedShares == 0) {
            return;
        }

        userWithdrawalSignal.commitedShares = 0;
        userWithdrawalSignal.targetEpoch = 0;
        userWithdrawalSignal.compound = false;

        IJonesGlpRewardTracker tracker = stableRewardTracker;

        jonesRewards.stake(msg.sender, snapshotCommitedShares);

        if (_compound) {
            stableCompoundRewards.compound();
            IJonesGlpCompoundRewards compounder = rewardCompounder[address(stable)];
            IERC20(address(glpStableVault)).approve(address(compounder), snapshotCommitedShares);
            compounder.deposit(snapshotCommitedShares, msg.sender);
        } else {
            IERC20(address(glpStableVault)).approve(address(tracker), snapshotCommitedShares);
            tracker.stake(msg.sender, snapshotCommitedShares);
        }

        // Update struct storage
        userSignal[msg.sender][_epoch] = userWithdrawalSignal;

        pendingWithdrawals = pendingWithdrawals - glpStableVault.previewRedeem(snapshotCommitedShares);

        emit CancelStableWithdrawalSignal(msg.sender, snapshotCommitedShares, _compound);
    }

    /**
     * @notice Users can redeem stable assets from the system
     * @param _epoch Target epoch
     * @return Amount of stables reeemed
     */
    function redeemStable(uint256 _epoch) external whenNotEmergencyPaused returns (uint256) {
        bytes32 userRole = whitelistController.getUserRole(msg.sender);
        IWhitelistController.RoleInfo memory info = whitelistController.getRoleInfo(userRole);

        WithdrawalSignal memory userWithdrawalSignal = userSignal[msg.sender][_epoch];

        if (currentEpoch() < userWithdrawalSignal.targetEpoch || userWithdrawalSignal.targetEpoch == 0) {
            revert Errors.NotRightEpoch();
        }

        if (userWithdrawalSignal.redeemed) {
            revert Errors.WithdrawalAlreadyCompleted();
        }

        if (userWithdrawalSignal.commitedShares == 0) {
            revert Errors.WithdrawalWithNoShares();
        }

        uint256 stableAmount = glpStableVault.previewRedeem(userWithdrawalSignal.commitedShares);

        uint256 stablesFromVault = _borrowStables(stableAmount);

        uint256 gmxIncentive;

        // Only redeem from strategy if there is not enough on the vault
        if (stablesFromVault < stableAmount) {
            uint256 difference = stableAmount - stablesFromVault;
            gmxIncentive = (difference * strategy.getRedeemStableGMXIncentive(difference) * 1e8) / BASIS_POINTS;
            strategy.onStableRedeem(difference, difference - gmxIncentive);
        }

        uint256 remainderStables = stableAmount - gmxIncentive;

        IERC20 stableToken = stable;

        if (stableToken.balanceOf(address(this)) < remainderStables) {
            revert Errors.NotEnoughStables();
        }

        glpStableVault.burn(address(this), userWithdrawalSignal.commitedShares);

        WithdrawalSignal storage withdrawalSingal = userSignal[msg.sender][_epoch];
        withdrawalSingal.redeemed = true;

        uint256 retention = ((stableAmount * info.jUSDC_RETENTION) / BASIS_POINTS);

        uint256 realRetention = gmxIncentive < retention ? retention - gmxIncentive : 0;

        uint256 amountAfterRetention = remainderStables - realRetention;

        if (amountAfterRetention > 0) {
            stableToken.transfer(msg.sender, amountAfterRetention);
        }

        if (realRetention > 0) {
            stableToken.approve(address(stableRewardTracker), realRetention);
            stableRewardTracker.depositRewards(realRetention);
        }

        pendingWithdrawals = pendingWithdrawals - stableAmount;

        // Information needed to calculate stable retention
        emit RedeemStable(msg.sender, amountAfterRetention, retention, realRetention, userWithdrawalSignal.compound);

        return amountAfterRetention;
    }

    /**
     * @notice User & Whitelist contract can claim their rewards
     * @return Stable rewards comming from Stable deposits
     * @return ETH rewards comming from GLP deposits
     * @return Jones rewards comming from jones emission
     */
    function claimRewards() external returns (uint256, uint256, uint256) {
        strategy.claimGlpRewards();

        uint256 stableRewards = stableRewardTracker.claim(msg.sender);

        stable.transfer(msg.sender, stableRewards);

        uint256 glpRewards = glpRewardTracker.claim(msg.sender);

        IERC20(weth).transfer(msg.sender, glpRewards);

        uint256 _jonesRewards = jonesRewards.getReward(msg.sender);

        emit ClaimRewards(msg.sender, stableRewards, glpRewards, _jonesRewards);

        return (stableRewards, glpRewards, _jonesRewards);
    }

    /**
     * @notice User Compound rewards
     * @param _stableDeposits Amount of stable shares to compound
     * @param _glpDeposits Amount of glp shares to compound
     * @return Amount of USDC shares
     * @return Amount of GLP shares
     */
    function compoundRewards(uint256 _stableDeposits, uint256 _glpDeposits) external returns (uint256, uint256) {
        return (compoundStableRewards(_stableDeposits), compoundGlpRewards(_glpDeposits));
    }

    /**
     * @notice User UnCompound rewards
     * @param _stableDeposits Amount of stable shares to uncompound
     * @param _glpDeposits Amount of glp shares to uncompound
     * @return Amount of USDC shares
     * @return Amount of GLP shares
     */
    function unCompoundRewards(uint256 _stableDeposits, uint256 _glpDeposits, address _user)
        external
        returns (uint256, uint256)
    {
        return (unCompoundStableRewards(_stableDeposits), unCompoundGlpRewards(_glpDeposits, _user));
    }

    /**
     * @notice User Compound GLP rewards
     * @param _shares Amount of glp shares to compound
     * @return Amount of jGLP shares
     */
    function compoundGlpRewards(uint256 _shares) public returns (uint256) {
        glpCompoundRewards.compound();
        // claim rewards & mint GLP
        strategy.claimGlpRewards();
        uint256 rewards = glpRewardTracker.claim(msg.sender); // WETH

        uint256 rewardShares;
        if (rewards != 0) {
            IERC20(weth).approve(router.glpManager(), rewards);
            uint256 glpAmount = router.mintAndStakeGlp(weth, rewards, 0, 0);

            // vault deposit GLP to get jGLP
            glp.approve(address(glpVault), glpAmount);
            rewardShares = glpVault.deposit(glpAmount, address(this));
        }

        // withdraw jGlp
        uint256 currentShares = glpRewardTracker.withdraw(msg.sender, _shares);

        // Stake in Rewards Tracker & Deposit into compounder
        IJonesGlpCompoundRewards compounder = rewardCompounder[address(glp)];
        uint256 totalShares = currentShares + rewardShares;
        IERC20(address(glpVault)).approve(address(compounder), totalShares);
        uint256 shares = compounder.deposit(totalShares, msg.sender);

        emit CompoundGlp(msg.sender, totalShares);

        return shares;
    }

    /**
     * @notice User UnCompound GLP rewards
     * @param _shares Amount of glp shares to uncompound
     * @return Amount of GLP shares
     */
    function unCompoundGlpRewards(uint256 _shares, address _user) public returns (uint256) {
        glpCompoundRewards.compound();
        return _unCompoundGlp(_shares, _user);
    }

    /**
     * @notice User Compound Stable rewards
     * @param _shares Amount of stable shares to compound
     * @return Amount of jUSDC shares
     */
    function compoundStableRewards(uint256 _shares) public returns (uint256) {
        stableCompoundRewards.compound();
        // claim rewards & deposit USDC
        strategy.claimGlpRewards();
        uint256 rewards = stableRewardTracker.claim(msg.sender); // USDC

        // vault deposit USDC to get jUSDC
        uint256 rewardShares;
        if (rewards > 0) {
            stable.approve(address(glpStableVault), rewards);
            rewardShares = glpStableVault.deposit(rewards, address(this));
        }

        // withdraw jUSDC
        uint256 currentShares = stableRewardTracker.withdraw(msg.sender, _shares);

        // Stake in Rewards Tracker & Deposit into compounder
        IJonesGlpCompoundRewards compounder = rewardCompounder[address(stable)];
        uint256 totalShares = currentShares + rewardShares;
        IERC20(address(glpStableVault)).approve(address(compounder), totalShares);
        uint256 shares = compounder.deposit(totalShares, msg.sender);

        emit CompoundStables(msg.sender, totalShares);

        return shares;
    }

    /**
     * @notice User UnCompound rewards
     * @param _shares Amount of stable shares to uncompound
     * @return Amount of USDC shares
     */
    function unCompoundStableRewards(uint256 _shares) public returns (uint256) {
        stableCompoundRewards.compound();
        IJonesGlpCompoundRewards compounder = rewardCompounder[address(stable)];

        uint256 assets = compounder.previewRedeem(_shares);
        uint256 assetsDeposited = compounder.totalAssetsToDeposits(msg.sender, assets);

        uint256 difference = assets - assetsDeposited;
        if (difference > 0) {
            jonesRewards.stake(msg.sender, difference);
        }

        return _unCompoundStables(_shares);
    }

    // ============================= External functions ================================ //
    /**
     * @notice Return user withdrawal signal
     * @param user address of user
     * @param epoch address of user
     * @return Targe Epoch
     * @return Commited shares
     * @return Redeem boolean
     */
    function withdrawSignal(address user, uint256 epoch) external view returns (uint256, uint256, bool, bool) {
        WithdrawalSignal memory userWithdrawalSignal = userSignal[user][epoch];
        return (
            userWithdrawalSignal.targetEpoch,
            userWithdrawalSignal.commitedShares,
            userWithdrawalSignal.redeemed,
            userWithdrawalSignal.compound
        );
    }

    /**
     * @notice Return the max amount of GLP that can be deposit in order to be alaign with the target leverage
     * @return GLP Cap
     */
    function getMaxCapGlp() public view returns (uint256) {
        return (glpStableVault.tvl() * BASIS_POINTS) / (strategy.getTargetLeverage() - BASIS_POINTS); // 18 decimals
    }

    // ============================= Governor functions ================================ //
    /**
     * @notice Set exit cooldown length in days
     * @param _days amount of days a user needs to wait to withdraw his stables
     */
    function setExitCooldown(uint256 _days) external onlyGovernor {
        EXIT_COOLDOWN = _days * EPOCH_DURATION;
    }

    /**
     * @notice Set Jones Rewards Contract
     * @param _jonesRewards Contract that manage Jones Rewards
     */
    function setJonesRewards(GlpJonesRewards _jonesRewards) external onlyGovernor {
        address previousAddress = address(jonesRewards);
        jonesRewards = _jonesRewards;
        emit SetJonesRewards(previousAddress, address(_jonesRewards));
    }

    /**
     * @notice Set Leverage Strategy Contract
     * @param _leverageStrategy Leverage Strategy address
     */
    function setLeverageStrategy(address _leverageStrategy) external onlyGovernor {
        address previousAddress = address(strategy);
        strategy = IJonesGlpLeverageStrategy(_leverageStrategy);
        emit SetJonesLeverageStrategy(previousAddress, _leverageStrategy);
    }

    /**
     * @notice Set Stable Compound Contract
     * @param _stableCompoundRewards Stable Compound address
     */
    function setStableCompoundRewards(address _stableCompoundRewards) external onlyGovernor {
        address oldStableCompoundRewards = address(stableCompoundRewards);
        stableCompoundRewards = IJonesGlpCompoundRewards(_stableCompoundRewards);
        rewardCompounder[address(stable)] = stableCompoundRewards;
        emit UpdateStableCompoundRewards(oldStableCompoundRewards, _stableCompoundRewards);
    }

    /**
     * @notice Set GLP Compound Contract
     * @param _glpCompoundRewards GLP Compound address
     */
    function setGlpCompoundRewards(address _glpCompoundRewards) external onlyGovernor {
        address oldGlpCompoundRewards = address(glpCompoundRewards);
        glpCompoundRewards = IJonesGlpCompoundRewards(_glpCompoundRewards);
        rewardCompounder[address(glp)] = glpCompoundRewards;
        emit UpdateGlpCompoundRewards(oldGlpCompoundRewards, _glpCompoundRewards);
    }

    /**
     * @notice Set Stable Tracker Contract
     * @param _stableRewardTracker Stable Tracker address
     */
    function setStableRewardTracker(address _stableRewardTracker) external onlyGovernor {
        address oldStableRewardTracker = address(stableRewardTracker);
        stableRewardTracker = IJonesGlpRewardTracker(_stableRewardTracker);
        rewardTrackers[address(stable)] = stableRewardTracker;
        emit UpdateStableRewardTracker(oldStableRewardTracker, _stableRewardTracker);
    }

    /**
     * @notice Set GLP Tracker Contract
     * @param _glpRewardTracker GLP Tracker address
     */
    function setGlpRewardTracker(address _glpRewardTracker) external onlyGovernor {
        address oldGlpRewardTracker = address(glpRewardTracker);
        glpRewardTracker = IJonesGlpRewardTracker(_glpRewardTracker);
        rewardTrackers[address(glp)] = glpRewardTracker;
        emit UpdateGlpRewardTracker(oldGlpRewardTracker, _glpRewardTracker);
    }

    /**
     * @notice Set a new incentive Receiver address
     * @param _newIncentiveReceiver Incentive Receiver Address
     */
    function setIncentiveReceiver(address _newIncentiveReceiver) external onlyGovernor {
        if (_newIncentiveReceiver == address(0)) {
            revert Errors.AddressCannotBeZeroAddress();
        }

        address oldIncentiveReceiver = address(incentiveReceiver);
        incentiveReceiver = IIncentiveReceiver(_newIncentiveReceiver);

        emit UpdateIncentiveReceiver(oldIncentiveReceiver, _newIncentiveReceiver);
    }

    /**
     * @notice Set GLP Adapter Contract
     * @param _adapter GLP Adapter address
     */
    function setGlpAdapter(address _adapter) external onlyGovernor {
        if (_adapter == address(0)) {
            revert Errors.AddressCannotBeZeroAddress();
        }

        address oldAdapter = address(adapter);
        adapter = GlpAdapter(_adapter);

        emit UpdateAdapter(oldAdapter, _adapter);
    }

    // ============================= Private functions ================================ //

    function _deposit(IERC4626 _vault, address _caller, uint256 _assets, bool compound)
        private
        returns (uint256, uint256)
    {
        IERC20 asset = IERC20(_vault.asset());
        address adapterAddress = address(adapter);
        IJonesGlpRewardTracker tracker = rewardTrackers[address(asset)];

        if (msg.sender == adapterAddress) {
            asset.transferFrom(adapterAddress, address(this), _assets);
        } else {
            asset.transferFrom(_caller, address(this), _assets);
        }

        uint256 vaultShares = _vaultDeposit(_vault, _assets);

        uint256 compoundShares;

        if (compound) {
            IJonesGlpCompoundRewards compounder = rewardCompounder[address(asset)];
            IERC20(address(_vault)).approve(address(compounder), vaultShares);
            compoundShares = compounder.deposit(vaultShares, _caller);
        } else {
            IERC20(address(_vault)).approve(address(tracker), vaultShares);
            tracker.stake(_caller, vaultShares);
        }

        return (compoundShares, vaultShares);
    }

    function _distributeGlp(uint256 _amount, address _dest, bool _compound) private returns (uint256) {
        uint256 retention = _chargeIncentive(_amount, _dest);
        uint256 wethAmount;

        if (retention > 0) {
            wethAmount = router.unstakeAndRedeemGlp(weth, retention, 0, address(this));
            uint256 jonesRetention = (wethAmount * 2) / 3;
            IERC20(weth).approve(address(incentiveReceiver), jonesRetention);
            incentiveReceiver.deposit(weth, jonesRetention);
            IERC20(weth).approve(address(glpRewardTracker), wethAmount - jonesRetention);

            glpRewardTracker.depositRewards(wethAmount - jonesRetention);
        }

        uint256 glpAfterRetention = _amount - retention;

        glp.transfer(_dest, glpAfterRetention);

        // Information needed to calculate glp retention
        emit RedeemGlp(_dest, glpAfterRetention, retention, wethAmount, _compound);

        return glpAfterRetention;
    }

    function _distributeGlpAdapter(uint256 _amount, address _dest, address _token, bool _native, bool _compound)
        private
        returns (uint256)
    {
        uint256 retention = _chargeIncentive(_amount, _dest);

        uint256 wethAmount;

        if (retention > 0) {
            wethAmount = router.unstakeAndRedeemGlp(weth, retention, 0, address(this));
            uint256 jonesRetention = (wethAmount * 2) / 3;
            IERC20(weth).approve(address(incentiveReceiver), jonesRetention);
            incentiveReceiver.deposit(weth, jonesRetention);
            IERC20(weth).approve(address(glpRewardTracker), wethAmount - jonesRetention);

            glpRewardTracker.depositRewards(wethAmount - jonesRetention);
        }

        if (_native) {
            uint256 ethAmount = router.unstakeAndRedeemGlpETH(_amount - retention, 0, payable(_dest));

            // Information needed to calculate glp retention
            emit RedeemGlpEth(_dest, _amount - retention, retention, wethAmount, ethAmount);

            return ethAmount;
        }

        uint256 assetAmount = router.unstakeAndRedeemGlp(_token, _amount - retention, 0, _dest);

        // Information needed to calculate glp retention
        emit RedeemGlpBasket(_dest, _amount - retention, retention, wethAmount, _token, _compound);

        return assetAmount;
    }

    function currentEpoch() public view returns (uint256) {
        return (block.timestamp / EPOCH_DURATION) * EPOCH_DURATION;
    }

    function _borrowStables(uint256 _amount) private returns (uint256) {
        JonesGlpStableVault stableVault = glpStableVault;

        uint256 balance = stable.balanceOf(address(stableVault));
        if (balance == 0) {
            return 0;
        }

        uint256 amountToBorrow = balance < _amount ? balance : _amount;

        emit BorrowStables(amountToBorrow);

        return stableVault.borrow(amountToBorrow);
    }

    function _chargeIncentive(uint256 _withdrawAmount, address _sender) private returns (uint256) {
        bytes32 userRole = whitelistController.getUserRole(_sender);
        IWhitelistController.RoleInfo memory info = whitelistController.getRoleInfo(userRole);

        uint256 retention = (_withdrawAmount * info.jGLP_RETENTION) / BASIS_POINTS;

        emit RetentionCharged(retention);
        return retention;
    }

    function _unCompoundGlp(uint256 _shares, address _user) private returns (uint256) {
        IJonesGlpCompoundRewards compounder = rewardCompounder[address(glp)];

        uint256 shares = compounder.redeem(_shares, _user);

        emit unCompoundGlp(_user, _shares);

        return shares;
    }

    function _unCompoundStables(uint256 _shares) private returns (uint256) {
        IJonesGlpCompoundRewards compounder = rewardCompounder[address(stable)];

        uint256 shares = compounder.redeem(_shares, msg.sender);

        emit unCompoundStables(msg.sender, _shares);

        return shares;
    }

    function _vaultDeposit(IERC4626 _vault, uint256 _assets) private returns (uint256) {
        address asset = _vault.asset();
        address vaultAddress = address(_vault);
        uint256 vaultShares;
        if (_vault.asset() == address(glp)) {
            uint256 glpMintIncentives = strategy.glpMintIncentive(_assets);

            uint256 assetsToDeposit = _assets - glpMintIncentives;

            IERC20(asset).approve(vaultAddress, assetsToDeposit);

            vaultShares = _vault.deposit(assetsToDeposit, address(this));
            if (glpMintIncentives > 0) {
                glp.transfer(vaultAddress, glpMintIncentives);
            }

            emit VaultDeposit(vaultAddress, _assets, glpMintIncentives);
        } else {
            IERC20(asset).approve(vaultAddress, _assets);
            vaultShares = _vault.deposit(_assets, address(this));
            emit VaultDeposit(vaultAddress, _assets, 0);
        }
        return vaultShares;
    }

    function _onlyInternalContract() private view {
        if (!whitelistController.isInternalContract(msg.sender)) {
            revert Errors.CallerIsNotInternalContract();
        }
    }

    function _onlyEOA() private view {
        if (msg.sender != tx.origin && !whitelistController.isWhitelistedContract(msg.sender)) {
            revert Errors.CallerIsNotWhitelisted();
        }
    }

    function togglePause() external onlyGovernor {
        if (paused()) {
            _unpause();
            return;
        }

        _pause();
    }

    function toggleEmergencyPause() external onlyGovernor {
        if (emergencyPaused()) {
            _emergencyUnpause();
            return;
        }

        _emergencyPause();
    }

    event UpdateIncentiveReceiver(address _oldIncentiveReceiver, address _newIncentiveReceiver);
    event UpdateStableRewardTracker(address _oldStableTracker, address _newStableTracker);
    event UpdateGlpRewardTracker(address _oldGlpTracker, address _newGlpTracker);
    event UpdateStableCompoundRewards(address _oldStableCompounder, address _newStableCompounder);
    event UpdateGlpCompoundRewards(address _oldGlpeCompounder, address _newGlpCompounder);
    event UpdateWithdrawalRetention(uint256 _newRetention);
    event UpdateAdapter(address _oldAdapter, address _newAdapter);
    event UpdateGlpVault(address _oldGlpVault, address _newGlpVault);
    event UpdateStableVault(address _oldStableVault, address _newStableVault);
    event UpdateStableAddress(address _oldStableAddress, address _newStableAddress);
    event UpdateGlpAddress(address _oldGlpAddress, address _newGlpAddress);
    event DepositGlp(address indexed _to, uint256 _amount, uint256 _sharesReceived, bool _compound);
    event DepositStables(address indexed _to, uint256 _amount, uint256 _sharesReceived, bool _compound);
    event VaultDeposit(address indexed vault, uint256 _amount, uint256 _retention);
    event RedeemGlpBasket(
        address indexed _to,
        uint256 _amount,
        uint256 _retentions,
        uint256 _ethRetentions,
        address _token,
        bool _compound
    );
    event RedeemGlpEth(
        address indexed _to, uint256 _amount, uint256 _retentions, uint256 _ethRetentions, uint256 _ethAmount
    );
    event RedeemGlp(address indexed _to, uint256 _amount, uint256 _retentions, uint256 _ethRetentions, bool _compound);
    event RedeemStable(
        address indexed _to, uint256 _amount, uint256 _retentions, uint256 _realRetentions, bool _compound
    );
    event ClaimRewards(address indexed _to, uint256 _stableAmount, uint256 _wEthAmount, uint256 _amountJones);
    event CompoundGlp(address indexed _to, uint256 _amount);
    event CompoundStables(address indexed _to, uint256 _amount);
    event unCompoundGlp(address indexed _to, uint256 _amount);
    event unCompoundStables(address indexed _to, uint256 _amount);
    event SettleEpoch(uint256 _currentEpochTs, uint256 indexed _targetEpochTs);
    event StableWithdrawalSignal(
        address indexed sender, uint256 _shares, uint256 indexed _targetEpochTs, bool _compound
    );
    event CancelStableWithdrawalSignal(address indexed sender, uint256 _shares, bool _compound);
    event RetentionCharged(uint256 indexed _retentions);
    event BorrowStables(uint256 indexed _amountBorrowed);
    event SetJonesRewards(address indexed _previousAddress, address indexed _newAddress);
    event SetJonesLeverageStrategy(address indexed _previousADdress, address indexed _newAddress);
}

File 27 of 51 : GlpJonesRewards.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 JonesDAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {ReentrancyGuard} from "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {Operable, Governable} from "../../common/Operable.sol";

contract GlpJonesRewards is Operable, ReentrancyGuard {
    IERC20 public immutable rewardsToken;

    // Duration of rewards to be paid out (in seconds)
    uint256 public duration;
    // Timestamp of when the rewards finish
    uint256 public finishAt;
    // Minimum of last updated time and reward finish time
    uint256 public updatedAt;
    // Reward to be paid out per second
    uint256 public rewardRate;
    // Sum of (reward rate * dt * 1e18 / total supply)
    uint256 public rewardPerTokenStored;
    // User address => rewardPerTokenStored
    mapping(address => uint256) public userRewardPerTokenPaid;
    // User address => rewards to be claimed
    mapping(address => uint256) public rewards;

    // Total staked
    uint256 public totalSupply;
    // User address => staked amount
    mapping(address => uint256) public balanceOf;

    constructor(address _rewardToken) Governable(msg.sender) ReentrancyGuard() {
        rewardsToken = IERC20(_rewardToken);
    }

    // ============================= Modifiers ================================ //

    modifier updateReward(address _account) {
        rewardPerTokenStored = rewardPerToken();
        updatedAt = lastTimeRewardApplicable();

        if (_account != address(0)) {
            rewards[_account] = earned(_account);
            userRewardPerTokenPaid[_account] = rewardPerTokenStored;
        }

        _;
    }

    // ============================= Operator functions ================================ //

    /**
     * @notice Virtual Stake, an accountability of the deposit
     * @dev No asset are transferred here is it just the accountability
     * @param _user Address of depositor
     * @param _amount Amount deposited
     */
    function stake(address _user, uint256 _amount) external onlyOperator updateReward(_user) {
        if (_amount == 0) {
            revert ZeroAmount();
        }
        balanceOf[_user] += _amount;
        totalSupply += _amount;

        emit Stake(_user, _amount);
    }

    /**
     * @notice Virtual withdraw, an accountability of the withdraw
     * @dev No asset have to be transfer here is it just the accountability
     * @param _user Address of withdrawal
     * @param _amount Amount to withdraw
     */
    function withdraw(address _user, uint256 _amount) external onlyOperator updateReward(_user) {
        if (_amount == 0) {
            revert ZeroAmount();
        }
        balanceOf[_user] -= _amount;
        totalSupply -= _amount;

        emit Withdraw(_user, _amount);
    }

    /**
     * @notice Transfer respective rewards, Jones emissions, to the _user address
     * @param _user Address where the rewards are transferred
     * @return Amount of rewards, Jones emissions
     */
    function getReward(address _user) external onlyOperator updateReward(_user) nonReentrant returns (uint256) {
        uint256 reward = rewards[_user];
        if (reward > 0) {
            rewards[_user] = 0;
            rewardsToken.transfer(_user, reward);
        }

        emit GetReward(_user, reward);

        return reward;
    }

    // ============================= Public functions ================================ //

    /**
     * @notice Return the last time a reward was applie
     * @return Timestamp when the last reward happened
     */
    function lastTimeRewardApplicable() public view returns (uint256) {
        return _min(finishAt, block.timestamp);
    }

    /**
     * @notice Return the amount of reward per tokend deposited
     * @return Amount of rewards, jones emissions
     */
    function rewardPerToken() public view returns (uint256) {
        if (totalSupply == 0) {
            return rewardPerTokenStored;
        }

        return rewardPerTokenStored + (rewardRate * (lastTimeRewardApplicable() - updatedAt) * 1e18) / totalSupply;
    }

    /**
     * @notice Return the total jones emissions earned by an user
     * @return Total emissions earned
     */
    function earned(address _user) public view returns (uint256) {
        return ((balanceOf[_user] * (rewardPerToken() - userRewardPerTokenPaid[_user])) / 1e18) + rewards[_user];
    }

    // ============================= Governor functions ================================ //

    /**
     * @notice Set the duration of the rewards
     * @param _duration timestamp based duration
     */
    function setRewardsDuration(uint256 _duration) external onlyGovernor {
        if (block.timestamp <= finishAt) {
            revert DurationNotFinished();
        }

        duration = _duration;

        emit UpdateRewardsDuration(finishAt, _duration + block.timestamp);
    }

    /**
     * @notice Notify Reward Amount for a specific _amount
     * @param _amount AMount to calculate the rewards
     */
    function notifyRewardAmount(uint256 _amount) external onlyGovernor updateReward(address(0)) {
        if (block.timestamp >= finishAt) {
            rewardRate = _amount / duration;
        } else {
            uint256 remainingRewards = (finishAt - block.timestamp) * rewardRate;
            rewardRate = (_amount + remainingRewards) / duration;
        }

        if (rewardRate == 0) {
            revert ZeroRewardRate();
        }
        if (rewardRate * duration > rewardsToken.balanceOf(address(this))) {
            revert NotEnoughBalance();
        }

        finishAt = block.timestamp + duration;
        updatedAt = block.timestamp;

        emit NotifyRewardAmount(_amount, finishAt);
    }

    // ============================= Private functions ================================ //
    function _min(uint256 x, uint256 y) private pure returns (uint256) {
        return x <= y ? x : y;
    }

    // ============================= Events ================================ //

    event Stake(address indexed _to, uint256 _amount);
    event Withdraw(address indexed _to, uint256 _amount);
    event GetReward(address indexed _to, uint256 _rewards);
    event UpdateRewardsDuration(uint256 _oldEnding, uint256 _newEnding);
    event NotifyRewardAmount(uint256 _amount, uint256 _finishAt);

    // ============================= Errors ================================ //

    error ZeroAmount();
    error ZeroRewardRate();
    error NotEnoughBalance();
    error DurationNotFinished();
}

File 28 of 51 : JonesGlpLeverageStrategy.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 Jones DAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {ReentrancyGuard} from "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";
import {Governable, OperableKeepable} from "../../common/OperableKeepable.sol";
import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IJonesBorrowableVault} from "../../interfaces/IJonesBorrowableVault.sol";
import {IJonesUsdVault} from "../../interfaces/IJonesUsdVault.sol";
import {IJonesGlpRewardDistributor} from "../../interfaces/IJonesGlpRewardDistributor.sol";
import {IAggregatorV3} from "../../interfaces/IAggregatorV3.sol";
import {IGmxRewardRouter} from "../../interfaces/IGmxRewardRouter.sol";
import {IJonesGlpLeverageStrategy} from "../../interfaces/IJonesGlpLeverageStrategy.sol";
import {IGlpManager} from "../../../src/interfaces/IGlpManager.sol";
import {IGMXVault} from "../../../src/interfaces/IGMXVault.sol";
import {IRewardTracker} from "../../../src/interfaces/IRewardTracker.sol";

contract JonesGlpLeverageStrategy is IJonesGlpLeverageStrategy, OperableKeepable, ReentrancyGuard {
    using Math for uint256;

    struct LeverageConfig {
        uint256 target;
        uint256 min;
        uint256 max;
    }

    IGmxRewardRouter constant routerV1 = IGmxRewardRouter(0xA906F338CB21815cBc4Bc87ace9e68c87eF8d8F1);
    IGmxRewardRouter constant routerV2 = IGmxRewardRouter(0xB95DB5B167D75e6d04227CfFFA61069348d271F5);
    IGlpManager constant glpManager = IGlpManager(0x3963FfC9dff443c2A94f21b129D429891E32ec18);
    address constant weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;

    uint256 public constant PRECISION = 1e30;
    uint256 public constant BASIS_POINTS = 1e12;
    uint256 public constant GMX_BASIS = 1e4;
    uint256 public constant USDC_DECIMALS = 1e6;
    uint256 public constant GLP_DECIMALS = 1e18;

    IERC20 public stable;
    IERC20 public glp;

    IJonesBorrowableVault stableVault;
    IJonesBorrowableVault glpVault;

    IJonesGlpRewardDistributor distributor;

    uint256 public stableDebt;

    LeverageConfig public leverageConfig;

    constructor(
        IJonesBorrowableVault _stableVault,
        IJonesBorrowableVault _glpVault,
        IJonesGlpRewardDistributor _distributor,
        LeverageConfig memory _leverageConfig,
        address _glp,
        address _stable
    ) Governable(msg.sender) ReentrancyGuard() {
        stableVault = _stableVault;
        glpVault = _glpVault;
        distributor = _distributor;

        stable = IERC20(_stable);
        glp = IERC20(_glp);

        _setLeverageConfig(_leverageConfig);
    }

    // ============================= Operator functions ================================ //

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function onGlpDeposit(uint256 _amount) external nonReentrant onlyOperator {
        _borrowGlp(_amount);
        if (leverage() < getTargetLeverage()) {
            _leverage(_amount);
        }
        _rebalance(getUnderlyingGlp());
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function onGlpRedeem(uint256 _amount) external nonReentrant onlyOperator returns (uint256) {
        if (_amount > getUnderlyingGlp()) {
            revert NotEnoughUnderlyingGlp();
        }

        uint256 glpRedeemRetention = glpRedeemRetention(_amount);
        uint256 assetsToRedeem = _amount - glpRedeemRetention;

        glp.transfer(msg.sender, assetsToRedeem);

        uint256 underlying = getUnderlyingGlp();
        uint256 leverageAmount = glp.balanceOf(address(this)) - underlying;
        uint256 protocolExcess = ((underlying * (leverageConfig.target - BASIS_POINTS)) / BASIS_POINTS);
        uint256 excessGlp;
        if (leverageAmount < protocolExcess) {
            excessGlp = leverageAmount;
        } else {
            excessGlp = ((_amount * (leverageConfig.target - BASIS_POINTS)) / BASIS_POINTS); // 18 Decimals
        }

        if (leverageAmount >= excessGlp && leverage() > getTargetLeverage()) {
            _deleverage(excessGlp);
        }

        underlying = getUnderlyingGlp();
        if (underlying > 0) {
            _rebalance(underlying);
        }

        emit Deleverage(excessGlp, assetsToRedeem);

        return assetsToRedeem;
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function onStableDeposit() external nonReentrant onlyOperator {
        _rebalance(getUnderlyingGlp());
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function onStableRedeem(uint256 _amount, uint256 _amountAfterRetention) external onlyOperator returns (uint256) {
        (uint256 glpAmount,) = _getRequiredGlpAmount(_amountAfterRetention + 2);

        routerV2.unstakeAndRedeemGlp(address(stable), glpAmount, _amountAfterRetention, address(this));

        stable.transfer(msg.sender, _amountAfterRetention);

        stableDebt = stableDebt - _amount;

        return _amountAfterRetention;
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function claimGlpRewards() external nonReentrant onlyOperatorOrKeeper {
        routerV1.handleRewards(false, false, true, true, true, true, false);

        uint256 rewards = IERC20(weth).balanceOf(address(this));

        uint256 currentLeverage = leverage();

        IERC20(weth).approve(address(distributor), rewards);
        distributor.splitRewards(rewards, currentLeverage, utilization());

        // Information needed to calculate rewards per Vault
        emit ClaimGlpRewards(
            tx.origin,
            msg.sender,
            rewards,
            block.timestamp,
            currentLeverage,
            glp.balanceOf(address(this)),
            getUnderlyingGlp(),
            glpVault.totalSupply(),
            stableDebt,
            stableVault.totalSupply()
            );
    }

    // ============================= Public functions ================================ //

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function utilization() public view returns (uint256) {
        uint256 borrowed = stableDebt;
        uint256 available = stable.balanceOf(address(stableVault));
        uint256 total = borrowed + available;

        if (total == 0) {
            return 0;
        }

        return (borrowed * BASIS_POINTS) / total;
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function leverage() public view returns (uint256) {
        uint256 glpTvl = getUnderlyingGlp(); // 18 Decimals

        if (glpTvl == 0) {
            return 0;
        }

        if (stableDebt == 0) {
            return 1 * BASIS_POINTS;
        }

        return ((glp.balanceOf(address(this)) * BASIS_POINTS) / glpTvl); // 12 Decimals;
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function getUnderlyingGlp() public view returns (uint256) {
        uint256 currentBalance = glp.balanceOf(address(this));

        if (currentBalance == 0) {
            return 0;
        }

        if (stableDebt > 0) {
            (uint256 glpAmount,) = _getRequiredGlpAmount(stableDebt + 2);
            return currentBalance > glpAmount ? currentBalance - glpAmount : 0;
        } else {
            return currentBalance;
        }
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function getStableGlpValue(uint256 _glpAmount) public view returns (uint256) {
        (uint256 _value,) = _sellGlpStableSimulation(_glpAmount);
        return _value;
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function buyGlpStableSimulation(uint256 _stableAmount) public view returns (uint256) {
        return _buyGlpStableSimulation(_stableAmount);
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function getRequiredStableAmount(uint256 _glpAmount) external view returns (uint256) {
        (uint256 stableAmount,) = _getRequiredStableAmount(_glpAmount);
        return stableAmount;
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function getRequiredGlpAmount(uint256 _stableAmount) external view returns (uint256) {
        (uint256 glpAmount,) = _getRequiredGlpAmount(_stableAmount);
        return glpAmount;
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function getRedeemStableGMXIncentive(uint256 _stableAmount) external view returns (uint256) {
        (, uint256 gmxRetention) = _getRequiredGlpAmount(_stableAmount);
        return gmxRetention;
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function glpMintIncentive(uint256 _glpAmount) public view returns (uint256) {
        return _glpMintIncentive(_glpAmount);
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function glpRedeemRetention(uint256 _glpAmount) public view returns (uint256) {
        return _glpRedeemRetention(_glpAmount);
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function getMaxLeverage() public view returns (uint256) {
        return leverageConfig.max;
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function getMinLeverage() public view returns (uint256) {
        return leverageConfig.min;
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function getGMXCapDifference() public view returns (uint256) {
        return _getGMXCapDifference();
    }

    /**
     * @inheritdoc IJonesGlpLeverageStrategy
     */
    function getTargetLeverage() public view returns (uint256) {
        return leverageConfig.target;
    }

    // ============================= Governor functions ================================ //

    /**
     * @notice Set Leverage Configuration
     * @dev Precision is based on 1e12 as 1x leverage
     * @param _target Target leverage
     * @param _min Min Leverage
     * @param _max Max Leverage
     * @param rebalance_ If is true trigger a rebalance
     */
    function setLeverageConfig(uint256 _target, uint256 _min, uint256 _max, bool rebalance_) public onlyGovernor {
        _setLeverageConfig(LeverageConfig(_target, _min, _max));
        emit SetLeverageConfig(_target, _min, _max);
        if (rebalance_) {
            _rebalance(getUnderlyingGlp());
        }
    }

    /**
     * @notice Set new glp address
     * @param _glp GLP address
     */
    function setGlpAddress(address _glp) external onlyGovernor {
        address oldGlp = address(glp);
        glp = IERC20(_glp);
        emit UpdateGlpAddress(oldGlp, _glp);
    }

    /**
     * @notice Set new stable address
     * @param _stable Stable addresss
     */
    function setStableAddress(address _stable) external onlyGovernor {
        address oldStable = address(stable);
        stable = IERC20(_stable);
        emit UpdateStableAddress(oldStable, _stable);
    }

    /**
     * @notice Emergency withdraw GLP in this contract
     * @param _to address to send the funds
     */
    function emergencyWithdraw(address _to) external onlyGovernor {
        uint256 currentBalance = glp.balanceOf(address(this));

        if (currentBalance == 0) {
            return;
        }

        glp.transfer(_to, currentBalance);

        emit EmergencyWithdraw(_to, currentBalance);
    }

    /**
     * @notice GMX function to signal transfer position
     * @param _to address to send the funds
     * @param _gmxRouter address of gmx router with the function
     */
    function transferAccount(address _to, address _gmxRouter) external onlyGovernor {
        if (_to == address(0)) {
            revert ZeroAddressError();
        }

        IGmxRewardRouter(_gmxRouter).signalTransfer(_to);
    }

    /**
     * @notice GMX function to accept transfer position
     * @param _sender address to receive the funds
     * @param _gmxRouter address of gmx router with the function
     */
    function acceptAccountTransfer(address _sender, address _gmxRouter) external onlyGovernor {
        IGmxRewardRouter gmxRouter = IGmxRewardRouter(_gmxRouter);

        gmxRouter.acceptTransfer(_sender);
    }

    // ============================= Keeper functions ================================ //

    /**
     * @notice Using by the bot to rebalance if is it needed
     */
    function rebalance() external onlyKeeper {
        _rebalance(getUnderlyingGlp());
    }

    /**
     * @notice Deleverage & pay stable debt
     */
    function unwind() external onlyGovernorOrKeeper {
        _setLeverageConfig(LeverageConfig(BASIS_POINTS + 1, BASIS_POINTS, BASIS_POINTS + 2));
        _liquidate();
    }

    /**
     * @notice Using by the bot to leverage Up if is needed
     */
    function leverageUp(uint256 _stableAmount) external onlyKeeper {
        uint256 availableForBorrowing = stable.balanceOf(address(stableVault));

        if (availableForBorrowing == 0) {
            return;
        }

        uint256 oldLeverage = leverage();

        _stableAmount = _adjustToGMXCap(_stableAmount);

        if (_stableAmount < 1e4) {
            return;
        }

        if (availableForBorrowing < _stableAmount) {
            _stableAmount = availableForBorrowing;
        }

        uint256 stableToBorrow = _stableAmount - stable.balanceOf(address(this));

        stableVault.borrow(stableToBorrow);
        emit BorrowStable(stableToBorrow);

        stableDebt = stableDebt + stableToBorrow;

        address stableAsset = address(stable);
        IERC20(stableAsset).approve(routerV2.glpManager(), _stableAmount);
        routerV2.mintAndStakeGlp(stableAsset, _stableAmount, 0, 0);

        uint256 newLeverage = leverage();

        if (newLeverage > getMaxLeverage()) {
            revert OverLeveraged();
        }

        emit LeverageUp(stableDebt, oldLeverage, newLeverage);
    }

    /**
     * @notice Using by the bot to leverage Down if is needed
     */
    function leverageDown(uint256 _glpAmount) external onlyKeeper {
        uint256 oldLeverage = leverage();

        uint256 stablesReceived = routerV2.unstakeAndRedeemGlp(address(stable), _glpAmount, 0, address(this));

        uint256 currentStableDebt = stableDebt;

        if (stablesReceived <= currentStableDebt) {
            _repayStable(stablesReceived);
        } else {
            _repayStable(currentStableDebt);
        }

        uint256 newLeverage = leverage();

        if (newLeverage < getMinLeverage()) {
            revert UnderLeveraged();
        }

        emit LeverageDown(stableDebt, oldLeverage, newLeverage);
    }

    // ============================= Private functions ================================ //

    function _rebalance(uint256 _glpDebt) private {
        uint256 currentLeverage = leverage();

        LeverageConfig memory currentLeverageConfig = leverageConfig;

        if (currentLeverage < currentLeverageConfig.min) {
            uint256 missingGlp = (_glpDebt * (currentLeverageConfig.target - currentLeverage)) / BASIS_POINTS; // 18 Decimals

            (uint256 stableToDeposit,) = _getRequiredStableAmount(missingGlp); // 6 Decimals

            stableToDeposit = _adjustToGMXCap(stableToDeposit);

            if (stableToDeposit < 1e4) {
                return;
            }

            uint256 availableForBorrowing = stable.balanceOf(address(stableVault));

            if (availableForBorrowing == 0) {
                return;
            }

            if (availableForBorrowing < stableToDeposit) {
                stableToDeposit = availableForBorrowing;
            }

            uint256 stableToBorrow = stableToDeposit - stable.balanceOf(address(this));

            stableVault.borrow(stableToBorrow);
            emit BorrowStable(stableToBorrow);

            stableDebt = stableDebt + stableToBorrow;

            address stableAsset = address(stable);
            IERC20(stableAsset).approve(routerV2.glpManager(), stableToDeposit);
            routerV2.mintAndStakeGlp(stableAsset, stableToDeposit, 0, 0);

            emit Rebalance(_glpDebt, currentLeverage, leverage(), tx.origin);

            return;
        }

        if (currentLeverage > currentLeverageConfig.max) {
            uint256 excessGlp = (_glpDebt * (currentLeverage - currentLeverageConfig.target)) / BASIS_POINTS;

            uint256 stablesReceived = routerV2.unstakeAndRedeemGlp(address(stable), excessGlp, 0, address(this));

            uint256 currentStableDebt = stableDebt;

            if (stablesReceived <= currentStableDebt) {
                _repayStable(stablesReceived);
            } else {
                _repayStable(currentStableDebt);
            }

            emit Rebalance(_glpDebt, currentLeverage, leverage(), tx.origin);

            return;
        }

        return;
    }

    function _liquidate() private {
        if (stableDebt == 0) {
            return;
        }

        uint256 glpBalance = glp.balanceOf(address(this));

        (uint256 glpAmount,) = _getRequiredGlpAmount(stableDebt + 2);

        if (glpAmount > glpBalance) {
            glpAmount = glpBalance;
        }

        uint256 stablesReceived = routerV2.unstakeAndRedeemGlp(address(stable), glpAmount, 0, address(this));

        uint256 currentStableDebt = stableDebt;

        if (stablesReceived <= currentStableDebt) {
            _repayStable(stablesReceived);
        } else {
            _repayStable(currentStableDebt);
        }

        emit Liquidate(stablesReceived);
    }

    function _borrowGlp(uint256 _amount) private returns (uint256) {
        glpVault.borrow(_amount);

        emit BorrowGlp(_amount);

        return _amount;
    }

    function _repayStable(uint256 _amount) internal returns (uint256) {
        stable.approve(address(stableVault), _amount);

        uint256 updatedAmount = stableDebt - stableVault.repay(_amount);

        stableDebt = updatedAmount;

        return updatedAmount;
    }

    function _setLeverageConfig(LeverageConfig memory _config) private {
        if (
            _config.min >= _config.max || _config.min >= _config.target || _config.max <= _config.target
                || _config.min < BASIS_POINTS
        ) {
            revert InvalidLeverageConfig();
        }

        leverageConfig = _config;
    }

    function _getRequiredGlpAmount(uint256 _stableAmount) private view returns (uint256, uint256) {
        // Working as expected, will get the amount of glp nedeed to get a few less stables than expected
        // If you have to get an amount greater or equal of _stableAmount, use _stableAmount + 2
        IGlpManager manager = glpManager;
        IGMXVault vault = IGMXVault(manager.vault());

        address usdc = address(stable);

        uint256 usdcPrice = vault.getMaxPrice(usdc); // 30 decimals

        uint256 glpSupply = glp.totalSupply();

        uint256 glpPrice = manager.getAum(false).mulDiv(GLP_DECIMALS, glpSupply, Math.Rounding.Down); // 30 decimals

        uint256 usdgAmount = _stableAmount.mulDiv(usdcPrice, PRECISION, Math.Rounding.Down) * BASIS_POINTS; // 18 decimals

        uint256 glpAmount = _stableAmount.mulDiv(usdcPrice, glpPrice, Math.Rounding.Down) * BASIS_POINTS; // 18 decimals

        uint256 retentionBasisPoints =
            vault.getFeeBasisPoints(usdc, usdgAmount, vault.mintBurnFeeBasisPoints(), vault.taxBasisPoints(), false);

        uint256 glpRequired = (glpAmount * GMX_BASIS) / (GMX_BASIS - retentionBasisPoints);

        (uint256 theoreticalStables,) = _sellGlpStableSimulation(glpRequired);

        while (theoreticalStables < _stableAmount - 2) {
            retentionBasisPoints = retentionBasisPoints + 1;
            glpRequired = (glpAmount * GMX_BASIS) / (GMX_BASIS - retentionBasisPoints);
            (theoreticalStables,) = _sellGlpStableSimulation(glpRequired);
        }

        return (glpRequired, retentionBasisPoints);
    }

    function _getRequiredStableAmount(uint256 _glpAmount) private view returns (uint256, uint256) {
        // Working as expected, will get the amount of stables nedeed to get a few less glp than expected
        // If you have to get an amount greater or equal of _glpAmount, use _glpAmount + 2
        IGlpManager manager = glpManager;
        IGMXVault vault = IGMXVault(manager.vault());

        address usdc = address(stable);

        uint256 usdcPrice = vault.getMinPrice(usdc); // 30 decimals

        uint256 glpPrice = manager.getAum(true).mulDiv(GLP_DECIMALS, glp.totalSupply(), Math.Rounding.Down); // 30 decimals

        uint256 stableAmount = _glpAmount.mulDiv(glpPrice, usdcPrice, Math.Rounding.Down); // 18 decimals

        uint256 usdgAmount = _glpAmount.mulDiv(glpPrice, PRECISION, Math.Rounding.Down); // 18 decimals

        uint256 retentionBasisPoints =
            vault.getFeeBasisPoints(usdc, usdgAmount, vault.mintBurnFeeBasisPoints(), vault.taxBasisPoints(), true);

        return ((stableAmount * GMX_BASIS / (GMX_BASIS - retentionBasisPoints)) / BASIS_POINTS, retentionBasisPoints); // 18 decimals
    }

    function _leverage(uint256 _glpAmount) private {
        uint256 missingGlp = ((_glpAmount * (leverageConfig.target - BASIS_POINTS)) / BASIS_POINTS); // 18 Decimals

        (uint256 stableToDeposit,) = _getRequiredStableAmount(missingGlp); // 6 Decimals

        stableToDeposit = _adjustToGMXCap(stableToDeposit);

        if (stableToDeposit < 1e4) {
            return;
        }

        uint256 availableForBorrowing = stable.balanceOf(address(stableVault));

        if (availableForBorrowing == 0) {
            return;
        }

        if (availableForBorrowing < stableToDeposit) {
            stableToDeposit = availableForBorrowing;
        }

        uint256 stableToBorrow = stableToDeposit - stable.balanceOf(address(this));

        stableVault.borrow(stableToBorrow);
        emit BorrowStable(stableToBorrow);

        stableDebt = stableDebt + stableToBorrow;

        address stableAsset = address(stable);
        IERC20(stableAsset).approve(routerV2.glpManager(), stableToDeposit);
        uint256 glpMinted = routerV2.mintAndStakeGlp(stableAsset, stableToDeposit, 0, 0);

        emit Leverage(_glpAmount, glpMinted);
    }

    function _deleverage(uint256 _excessGlp) private returns (uint256) {
        uint256 stablesReceived = routerV2.unstakeAndRedeemGlp(address(stable), _excessGlp, 0, address(this));

        uint256 currentStableDebt = stableDebt;

        if (stablesReceived <= currentStableDebt) {
            _repayStable(stablesReceived);
        } else {
            _repayStable(currentStableDebt);
        }

        return stablesReceived;
    }

    function _adjustToGMXCap(uint256 _stableAmount) private view returns (uint256) {
        IGlpManager manager = glpManager;
        IGMXVault vault = IGMXVault(manager.vault());

        address usdc = address(stable);

        uint256 mintAmount = _buyGlpStableSimulation(_stableAmount);

        uint256 currentUsdgAmount = vault.usdgAmounts(usdc);

        uint256 nextAmount = currentUsdgAmount + mintAmount;
        uint256 maxUsdgAmount = vault.maxUsdgAmounts(usdc);

        if (nextAmount > maxUsdgAmount) {
            (uint256 requiredStables,) = _getRequiredStableAmount(maxUsdgAmount - currentUsdgAmount);
            return requiredStables;
        } else {
            return _stableAmount;
        }
    }

    function _getGMXCapDifference() private view returns (uint256) {
        IGlpManager manager = glpManager;
        IGMXVault vault = IGMXVault(manager.vault());

        address usdc = address(stable);

        uint256 currentUsdgAmount = vault.usdgAmounts(usdc);

        uint256 maxUsdgAmount = vault.maxUsdgAmounts(usdc);

        return maxUsdgAmount - currentUsdgAmount;
    }

    function _buyGlpStableSimulation(uint256 _stableAmount) private view returns (uint256) {
        IGlpManager manager = glpManager;
        IGMXVault vault = IGMXVault(manager.vault());

        address usdc = address(stable);

        uint256 aumInUsdg = manager.getAumInUsdg(true);

        uint256 usdcPrice = vault.getMinPrice(usdc); // 30 decimals

        uint256 usdgAmount = _stableAmount.mulDiv(usdcPrice, PRECISION); // 6 decimals

        usdgAmount = usdgAmount.mulDiv(GLP_DECIMALS, USDC_DECIMALS); // 18 decimals

        uint256 retentionBasisPoints =
            vault.getFeeBasisPoints(usdc, usdgAmount, vault.mintBurnFeeBasisPoints(), vault.taxBasisPoints(), true);

        uint256 amountAfterRetention = _stableAmount.mulDiv(GMX_BASIS - retentionBasisPoints, GMX_BASIS); // 6 decimals

        uint256 mintAmount = amountAfterRetention.mulDiv(usdcPrice, PRECISION); // 6 decimals

        mintAmount = mintAmount.mulDiv(GLP_DECIMALS, USDC_DECIMALS); // 18 decimals

        return aumInUsdg == 0 ? mintAmount : mintAmount.mulDiv(glp.totalSupply(), aumInUsdg); // 18 decimals
    }

    function _buyGlpStableSimulationWhitoutRetention(uint256 _stableAmount) private view returns (uint256) {
        IGlpManager manager = glpManager;
        IGMXVault vault = IGMXVault(manager.vault());

        address usdc = address(stable);

        uint256 aumInUsdg = manager.getAumInUsdg(true);

        uint256 usdcPrice = vault.getMinPrice(usdc); // 30 decimals

        uint256 usdgAmount = _stableAmount.mulDiv(usdcPrice, PRECISION); // 6 decimals

        usdgAmount = usdgAmount.mulDiv(GLP_DECIMALS, USDC_DECIMALS); // 18 decimals

        uint256 mintAmount = _stableAmount.mulDiv(usdcPrice, PRECISION); // 6 decimals

        mintAmount = mintAmount.mulDiv(GLP_DECIMALS, USDC_DECIMALS); // 18 decimals

        return aumInUsdg == 0 ? mintAmount : mintAmount.mulDiv(glp.totalSupply(), aumInUsdg); // 18 decimals
    }

    function _sellGlpStableSimulation(uint256 _glpAmount) private view returns (uint256, uint256) {
        IGlpManager manager = glpManager;
        IGMXVault vault = IGMXVault(manager.vault());

        address usdc = address(stable);

        uint256 usdgAmount = _glpAmount.mulDiv(manager.getAumInUsdg(false), glp.totalSupply());

        uint256 redemptionAmount = usdgAmount.mulDiv(PRECISION, vault.getMaxPrice(usdc));

        redemptionAmount = redemptionAmount.mulDiv(USDC_DECIMALS, GLP_DECIMALS); // 6 decimals

        uint256 retentionBasisPoints =
            _getGMXBasisRetention(usdc, usdgAmount, vault.mintBurnFeeBasisPoints(), vault.taxBasisPoints(), false);

        return (redemptionAmount.mulDiv(GMX_BASIS - retentionBasisPoints, GMX_BASIS), retentionBasisPoints);
    }

    function _glpMintIncentive(uint256 _glpAmount) private view returns (uint256) {
        uint256 amountToMint = _glpAmount.mulDiv(leverageConfig.target - BASIS_POINTS, BASIS_POINTS); // 18 Decimals
        (uint256 stablesNeeded, uint256 gmxIncentive) = _getRequiredStableAmount(amountToMint + 2);
        uint256 incentiveInStables = stablesNeeded.mulDiv(gmxIncentive, GMX_BASIS);
        return _buyGlpStableSimulationWhitoutRetention(incentiveInStables); // retention in glp
    }

    function _glpRedeemRetention(uint256 _glpAmount) private view returns (uint256) {
        uint256 amountToRedeem = _glpAmount.mulDiv(leverageConfig.target - BASIS_POINTS, BASIS_POINTS); //18
        (, uint256 gmxRetention) = _sellGlpStableSimulation(amountToRedeem + 2);
        uint256 retentionInGlp = amountToRedeem.mulDiv(gmxRetention, GMX_BASIS);
        return retentionInGlp;
    }

    function _getGMXBasisRetention(
        address _token,
        uint256 _usdgDelta,
        uint256 _retentionBasisPoints,
        uint256 _taxBasisPoints,
        bool _increment
    ) private view returns (uint256) {
        IGMXVault vault = IGMXVault(glpManager.vault());

        if (!vault.hasDynamicFees()) return _retentionBasisPoints;

        uint256 initialAmount = _increment ? vault.usdgAmounts(_token) : vault.usdgAmounts(_token) - _usdgDelta;

        uint256 nextAmount = initialAmount + _usdgDelta;
        if (!_increment) {
            nextAmount = _usdgDelta > initialAmount ? 0 : initialAmount - _usdgDelta;
        }

        uint256 targetAmount = vault.getTargetUsdgAmount(_token);
        if (targetAmount == 0) return _retentionBasisPoints;

        uint256 initialDiff = initialAmount > targetAmount ? initialAmount - targetAmount : targetAmount - initialAmount;
        uint256 nextDiff = nextAmount > targetAmount ? nextAmount - targetAmount : targetAmount - nextAmount;

        // action improves relative asset balance
        if (nextDiff < initialDiff) {
            uint256 rebateBps = _taxBasisPoints.mulDiv(initialDiff, targetAmount);
            return rebateBps > _retentionBasisPoints ? 0 : _retentionBasisPoints - rebateBps;
        }

        uint256 averageDiff = (initialDiff + nextDiff) / 2;
        if (averageDiff > targetAmount) {
            averageDiff = targetAmount;
        }
        uint256 taxBps = _taxBasisPoints.mulDiv(averageDiff, targetAmount);
        return _retentionBasisPoints + taxBps;
    }
}

File 29 of 51 : JonesBaseGlpVault.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 Jones DAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {ERC4626} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol";
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {JonesUsdVault} from "../../vaults/JonesUsdVault.sol";
import {JonesBorrowableVault} from "../../vaults/JonesBorrowableVault.sol";
import {JonesOperableVault} from "../../vaults/JonesOperableVault.sol";
import {JonesGovernableVault} from "../../vaults/JonesGovernableVault.sol";
import {IAggregatorV3} from "../../interfaces/IAggregatorV3.sol";
import {IStakedGlp} from "../../interfaces/IStakedGlp.sol";
import {IJonesGlpLeverageStrategy} from "../../interfaces/IJonesGlpLeverageStrategy.sol";

abstract contract JonesBaseGlpVault is JonesOperableVault, JonesUsdVault, JonesBorrowableVault {
    IJonesGlpLeverageStrategy public strategy;
    address internal receiver;

    constructor(IAggregatorV3 _oracle, IERC20Metadata _asset, string memory _name, string memory _symbol)
        JonesGovernableVault(msg.sender)
        JonesUsdVault(_oracle)
        ERC4626(_asset)
        ERC20(_name, _symbol)
    {}

    // ============================= Operable functions ================================ //

    /**
     * @dev See {openzeppelin-IERC4626-deposit}.
     */
    function deposit(uint256 _assets, address _receiver)
        public
        virtual
        override(JonesOperableVault, ERC4626)
        whenNotPaused
        returns (uint256)
    {
        return super.deposit(_assets, _receiver);
    }

    /**
     * @dev See {openzeppelin-IERC4626-mint}.
     */
    function mint(uint256 _shares, address _receiver)
        public
        override(JonesOperableVault, ERC4626)
        whenNotPaused
        returns (uint256)
    {
        return super.mint(_shares, _receiver);
    }

    /**
     * @dev See {openzeppelin-IERC4626-withdraw}.
     */
    function withdraw(uint256 _assets, address _receiver, address _owner)
        public
        virtual
        override(JonesOperableVault, ERC4626)
        returns (uint256)
    {
        return super.withdraw(_assets, _receiver, _owner);
    }

    /**
     * @dev See {openzeppelin-IERC4626-redeem}.
     */
    function redeem(uint256 _shares, address _receiver, address _owner)
        public
        virtual
        override(JonesOperableVault, ERC4626)
        returns (uint256)
    {
        return super.redeem(_shares, _receiver, _owner);
    }

    /**
     * @notice Set new strategy address
     * @param _strategy Strategy Contract
     */
    function setStrategyAddress(IJonesGlpLeverageStrategy _strategy) external onlyGovernor {
        strategy = _strategy;
    }

    function setExcessReceiver(address _receiver) external onlyGovernor {
        receiver = _receiver;
    }
}

File 30 of 51 : JonesGlpStableVault.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 Jones DAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {JonesBaseGlpVault} from "./JonesBaseGlpVault.sol";
import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IAggregatorV3} from "../../interfaces/IAggregatorV3.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

contract JonesGlpStableVault is JonesBaseGlpVault {
    uint256 public constant BASIS_POINTS = 1e12;

    constructor()
        JonesBaseGlpVault(
            IAggregatorV3(0x50834F3163758fcC1Df9973b6e91f0F0F0434aD3),
            IERC20Metadata(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8),
            "USDC Vault Receipt Token",
            "UVRT"
        )
    {}

    // ============================= Public functions ================================ //

    function deposit(uint256 _assets, address _receiver)
        public
        override(JonesBaseGlpVault)
        whenNotPaused
        returns (uint256)
    {
        _validate();
        return super.deposit(_assets, _receiver);
    }

    /**
     * @dev See {openzeppelin-IERC4626-_burn}.
     */
    function burn(address _user, uint256 _amount) public onlyOperator {
        _validate();
        _burn(_user, _amount);
    }

    /**
     * @notice Return total asset deposited
     * @return Amount of asset deposited
     */
    function totalAssets() public view override returns (uint256) {
        return super.totalAssets() + strategy.stableDebt();
    }

    // ============================= Governor functions ================================ //

    /**
     * @notice Emergency withdraw USDC in this contract
     * @param _to address to send the funds
     */
    function emergencyWithdraw(address _to) external onlyGovernor {
        IERC20 underlyingAsset = IERC20(super.asset());

        uint256 balance = underlyingAsset.balanceOf(address(this));

        if (balance == 0) {
            return;
        }

        underlyingAsset.transfer(_to, balance);
    }

    // ============================= Private functions ================================ //

    function _validate() private {
        uint256 shares = totalSupply() / BASIS_POINTS;
        uint256 assets = totalAssets();
        address stable = asset();

        if (assets > shares) {
            uint256 ratioExcess = assets - shares;
            IERC20(stable).transfer(receiver, ratioExcess);
        }
    }
}

File 31 of 51 : JonesGlpVault.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 Jones DAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {JonesBaseGlpVault} from "./JonesBaseGlpVault.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IAggregatorV3} from "../../interfaces/IAggregatorV3.sol";
import {IStakedGlp} from "../../interfaces/IStakedGlp.sol";
import {IJonesGlpLeverageStrategy} from "../../interfaces/IJonesGlpLeverageStrategy.sol";

contract JonesGlpVault is JonesBaseGlpVault {
    uint256 private freezedAssets;

    constructor()
        JonesBaseGlpVault(
            IAggregatorV3(0xDFE51CC551949704E5C52C7BB98DCC3fd934E7fa),
            IERC20Metadata(0x5402B5F40310bDED796c7D0F3FF6683f5C0cFfdf),
            "GLP Vault Receipt Token",
            "GVRT"
        )
    {}
    // ============================= Public functions ================================ //

    function deposit(uint256 _assets, address _receiver)
        public
        override(JonesBaseGlpVault)
        whenNotPaused
        returns (uint256)
    {
        _validate();
        return super.deposit(_assets, _receiver);
    }

    /**
     * @dev See {openzeppelin-IERC4626-_burn}.
     */
    function burn(address _user, uint256 _amount) public onlyOperator {
        _validate();
        _burn(_user, _amount);
    }

    /**
     * @notice Return total asset deposited
     * @return Amount of asset deposited
     */
    function totalAssets() public view override returns (uint256) {
        if (freezedAssets != 0) {
            return freezedAssets;
        }

        return super.totalAssets() + strategy.getUnderlyingGlp();
    }

    // ============================= Private functions ================================ //

    function _validate() private {
        IERC20 asset = IERC20(asset());
        uint256 balance = asset.balanceOf(address(this));

        if (balance > 0) {
            asset.transfer(receiver, balance);
        }
    }
}

File 32 of 51 : Errors.sol
//SPDX-License-Identifier:  MIT
pragma solidity ^0.8.10;

interface Errors {
    error AlreadyInitialized();
    error CallerIsNotInternalContract();
    error CallerIsNotWhitelisted();
    error InvalidWithdrawalRetention();
    error MaxGlpTvlReached();
    error CannotSettleEpochInFuture();
    error EpochAlreadySettled();
    error EpochNotSettled();
    error WithdrawalAlreadyCompleted();
    error WithdrawalWithNoShares();
    error WithdrawalSignalAlreadyDone();
    error NotRightEpoch();
    error NotEnoughStables();
    error NoEpochToSettle();
    error CannotCancelWithdrawal();
    error AddressCannotBeZeroAddress();
    error OnlyAdapter();
    error DoesntHavePermission();
}

File 33 of 51 : IAggregatorV3.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IAggregatorV3 {
    function decimals() external view returns (uint8);

    function description() external view returns (string memory);

    function version() external view returns (uint256);

    function getRoundData(uint80 _roundId)
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

    function latestRoundData()
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}

File 34 of 51 : IGMXVault.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface IGMXVault {
    function whitelistedTokens(address) external view returns (bool);

    function stableTokens(address) external view returns (bool);

    function shortableTokens(address) external view returns (bool);

    function getMaxPrice(address _token) external view returns (uint256);

    function getMinPrice(address _token) external view returns (uint256);

    function getPosition(address _account, address _collateralToken, address _indexToken, bool _isLong)
        external
        view
        returns (uint256, uint256, uint256, uint256, uint256, uint256, bool, uint256);

    function mintBurnFeeBasisPoints() external view returns (uint256);

    function taxBasisPoints() external view returns (uint256);

    function getFeeBasisPoints(
        address _token,
        uint256 _usdgDelta,
        uint256 _feeBasisPoints,
        uint256 _taxBasisPoints,
        bool _increment
    ) external view returns (uint256);

    function usdgAmounts(address _token) external view returns (uint256);
    function maxUsdgAmounts(address _token) external view returns (uint256);
    function hasDynamicFees() external view returns (bool);
    function getTargetUsdgAmount(address _token) external view returns (uint256);
}

File 35 of 51 : IGlpManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {IGMXVault} from "./IGMXVault.sol";

interface IGlpManager {
    function getAum(bool _maximize) external view returns (uint256);
    function getAumInUsdg(bool _maximize) external view returns (uint256);
    function vault() external view returns (address);
    function glp() external view returns (address);
}

File 36 of 51 : IGmxRewardRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IGmxRewardRouter {
    function mintAndStakeGlp(address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp)
        external
        returns (uint256);

    function unstakeAndRedeemGlp(address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver)
        external
        returns (uint256);

    function unstakeAndRedeemGlpETH(uint256 _glpAmount, uint256 _minOut, address payable _receiver)
        external
        returns (uint256);

    function glpManager() external view returns (address);

    function handleRewards(
        bool _shouldClaimGmx,
        bool _shouldStakeGmx,
        bool _shouldClaimEsGmx,
        bool _shouldStakeEsGmx,
        bool _shouldStakeMultiplierPoints,
        bool _shouldClaimWeth,
        bool _shouldConvertWethToEth
    ) external;

    function signalTransfer(address _receiver) external;
    function acceptTransfer(address _sender) external;
    function pendingReceivers(address input) external returns (address);
    function stakeEsGmx(uint256 _amount) external;
    function mintAndStakeGlpETH(uint256 _minUsdg, uint256 _minGlp) external payable returns (uint256);
}

File 37 of 51 : IIncentiveReceiver.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.2;

interface IIncentiveReceiver {
    function deposit(address _token, uint256 _amount) external;

    function addDepositor(address _depositor) external;
}

File 38 of 51 : IJonesBorrowableVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {IERC4626} from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol";

interface IJonesBorrowableVault is IERC4626 {
    function borrow(uint256 _amount) external returns (uint256);
    function repay(uint256 _amount) external returns (uint256);
}

File 39 of 51 : IJonesGlpCompoundRewards.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IJonesGlpCompoundRewards {
    event Deposit(address indexed _caller, address indexed receiver, uint256 _assets, uint256 _shares);
    event Withdraw(address indexed _caller, address indexed receiver, uint256 _assets, uint256 _shares);
    event Compound(uint256 _rewards, uint256 _totalAssets, uint256 _retentions);

    /**
     * @notice Deposit assets into this contract and get shares
     * @param assets Amount of assets to be deposit
     * @param receiver Address Owner of the deposit
     * @return Amount of shares minted
     */
    function deposit(uint256 assets, address receiver) external returns (uint256);

    /**
     * @notice Withdraw the deposited assets
     * @param shares Amount to shares to be burned to get the assets
     * @param receiver Address who will receive the assets
     * @return Amount of assets redemeed
     */
    function redeem(uint256 shares, address receiver) external returns (uint256);

    /**
     * @notice Claim cumulative rewards & stake them
     */
    function compound() external;

    /**
     * @notice Preview how many shares will obtain when deposit
     * @param assets Amount to shares to be deposit
     * @return Amount of shares to be minted
     */
    function previewDeposit(uint256 assets) external view returns (uint256);

    /**
     * @notice Preview how many assets will obtain when redeem
     * @param shares Amount to shares to be redeem
     * @return Amount of assets to be redeemed
     */
    function previewRedeem(uint256 shares) external view returns (uint256);

    /**
     * @notice Convert recipent compounded assets into un-compunding assets
     * @param assets Amount to be converted
     * @param recipient address of assets owner
     * @return Amount of un-compounding assets
     */
    function totalAssetsToDeposits(address recipient, uint256 assets) external view returns (uint256);

    error AddressCannotBeZeroAddress();
    error AmountCannotBeZero();
    error AmountExceedsStakedAmount();
    error RetentionPercentageOutOfRange();
}

File 40 of 51 : IJonesGlpLeverageStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IJonesGlpLeverageStrategy {
    /**
     * @notice React to a GLP deposit, borrow GLO from Vault & relabance
     * @param _amount Amount of GLP deposited
     */
    function onGlpDeposit(uint256 _amount) external;

    /**
     * @notice React to a GLP redeem, pay stable debt if is need and transfer GLP to the user
     * @param _amount Amount that the user is attempting to redeem
     * @return Amount of GLP to redeem
     */
    function onGlpRedeem(uint256 _amount) external returns (uint256);

    /**
     * @notice React to a Stable deposit, relabance if is needed
     */
    function onStableDeposit() external;

    /**
     * @notice Redeem GLP for stables
     * @param _amount Amount of stables to reduce from debt
     * @param _amountAfterRetention Amount of stables getting from redeem GLP
     * @return Amount of stables getting from redeem GLP
     */
    function onStableRedeem(uint256 _amount, uint256 _amountAfterRetention) external returns (uint256);

    /**
     * @notice Claim GLP rewards from GMX and split them
     */
    function claimGlpRewards() external;

    /**
     * @notice Return the current utilization of stable Vault
     * @dev Precision is based on 1e12 as 100% percent
     * @return The % of utilization
     */
    function utilization() external view returns (uint256);

    /**
     * @notice Return the current GLP leverage position
     * @dev Precision is based on 1e12 as 1x leverage
     * @return Leverage position
     */
    function leverage() external view returns (uint256);

    /**
     * @notice Return the amount of GLP that represent 1x of leverage
     * @return Amount of GLP
     */
    function getUnderlyingGlp() external view returns (uint256);

    /**
     * @notice Return the stable debt
     * @return Amount of stable debt
     */
    function stableDebt() external view returns (uint256);

    /**
     * @notice Get the stable value of sell _amount of GLP
     * @param _glpAmount Amount of GLP
     * @return Stables getting from _glpAmount of GLP
     */
    function getStableGlpValue(uint256 _glpAmount) external view returns (uint256);

    /**
     * @notice Get the simulated GLP amount minted with USDC
     * @param _stableAmount Amount of USDC
     * @return Stables Amount of simulated GLP
     */
    function buyGlpStableSimulation(uint256 _stableAmount) external view returns (uint256);

    /**
     * @notice Get the required USDC amount to mint _glpAmount of GLP
     * @param _glpAmount Amount of GLP to be minted
     * @return Amount of stables required to mint _glpAmount of GLP
     */
    function getRequiredStableAmount(uint256 _glpAmount) external view returns (uint256);

    /**
     * @notice Get the simulated GLP amount required to redeem _stableAmount of USDC
     * @param _stableAmount Amount of USDC
     * @return Stables Amount of simulated GLP amount required to redeem _stableAmount of USDC
     */
    function getRequiredGlpAmount(uint256 _stableAmount) external view returns (uint256);

    /**
     * @notice Get the simulated GLP mint retention on a glp deposit
     * @param _glpAmount Amount of GLP deposited
     * @return GLP Amount of retention
     */
    function glpMintIncentive(uint256 _glpAmount) external view returns (uint256);

    /**
     * @notice Get GMX incentive to redeem stables
     * @param _stableAmount Amount of stables
     * @return GMX retention to redeem stables
     */
    function getRedeemStableGMXIncentive(uint256 _stableAmount) external view returns (uint256);

    /**
     * @notice Return max leverage configuration
     * @return Max leverage
     */
    function getMaxLeverage() external view returns (uint256);

    /**
     * @notice Return min leverage configuration
     * @return Min leverage
     */
    function getMinLeverage() external view returns (uint256);

    /**
     * @notice Return target leverage configuration
     * @return Target leverage
     */
    function getTargetLeverage() external view returns (uint256);

    /**
     * @notice Return the amount of GLP to reach the GMX cap for USDC
     * @return Cap Difference
     */
    function getGMXCapDifference() external view returns (uint256);

    /**
     * @notice Get the simulated GLP redeem retention on a glp redeem
     * @param _glpAmount Amount of GLP redeemed
     * @return GLP Amount of retention
     */
    function glpRedeemRetention(uint256 _glpAmount) external view returns (uint256);

    event Rebalance(
        uint256 _glpDebt, uint256 indexed _currentLeverage, uint256 indexed _newLeverage, address indexed _sender
    );
    event GetUnderlyingGlp(uint256 _amount);
    event SetLeverageConfig(uint256 _target, uint256 _min, uint256 _max);
    event ClaimGlpRewards(
        address indexed _origin,
        address indexed _sender,
        uint256 _rewards,
        uint256 _timestamp,
        uint256 _leverage,
        uint256 _glpBalance,
        uint256 _underlyingGlp,
        uint256 _glpShares,
        uint256 _stableDebt,
        uint256 _stableShares
    );

    event Liquidate(uint256 indexed _stablesReceived);
    event BorrowGlp(uint256 indexed _amount);
    event BorrowStable(uint256 indexed _amount);
    event RepayStable(uint256 indexed _amount);
    event RepayGlp(uint256 indexed _amount);
    event EmergencyWithdraw(address indexed _to, uint256 indexed _amount);
    event UpdateStableAddress(address _oldStableAddress, address _newStableAddress);
    event UpdateGlpAddress(address _oldGlpAddress, address _newGlpAddress);
    event Leverage(uint256 _glpDeposited, uint256 _glpMinted);
    event LeverageUp(uint256 _stableDebt, uint256 _oldLeverage, uint256 _currentLeverage);
    event LeverageDown(uint256 _stableDebt, uint256 _oldLeverage, uint256 _currentLeverage);
    event Deleverage(uint256 _glpAmount, uint256 _glpRedeemed);

    error ZeroAddressError();
    error InvalidLeverageConfig();
    error InvalidSlippage();
    error ReachedSlippageTolerance();
    error OverLeveraged();
    error UnderLeveraged();
    error NotEnoughUnderlyingGlp();
}

File 41 of 51 : IJonesGlpRewardDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IJonesGlpRewardDistributor {
    event Distribute(uint256 amount);
    event SplitRewards(uint256 _glpRewards, uint256 _stableRewards, uint256 _jonesRewards);

    /**
     * @notice Send the pool rewards to the tracker
     * @dev This function is called from the Reward Tracker
     * @return Amount of rewards sent
     */
    function distributeRewards() external returns (uint256);

    /**
     * @notice Split the rewards comming from GMX
     * @param _amount of rewards to be splited
     * @param _leverage current strategy leverage
     * @param _utilization current stable pool utilization
     */
    function splitRewards(uint256 _amount, uint256 _leverage, uint256 _utilization) external;

    /**
     * @notice Return the pending rewards to be distributed of a pool
     * @param _pool Address of the Reward Tracker pool
     * @return Amount of pending rewards
     */
    function pendingRewards(address _pool) external view returns (uint256);

    error AddressCannotBeZeroAddress();
}

File 42 of 51 : IJonesGlpRewardTracker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IJonesGlpRewardTracker {
    event Stake(address indexed depositor, uint256 amount);
    event Withdraw(address indexed _account, uint256 _amount);
    event Claim(address indexed receiver, uint256 amount);
    event UpdateRewards(address indexed _account, uint256 _rewards, uint256 _totalShares, uint256 _rewardPerShare);

    /**
     * @notice Stake into this contract assets to start earning rewards
     * @param _account Owner of the stake and future rewards
     * @param _amount Assets to be staked
     * @return Amount of assets staked
     */
    function stake(address _account, uint256 _amount) external returns (uint256);

    /**
     * @notice Withdraw the staked assets
     * @param _account Owner of the assets to be withdrawn
     * @param _amount Assets to be withdrawn
     * @return Amount of assets witdrawed
     */
    function withdraw(address _account, uint256 _amount) external returns (uint256);

    /**
     * @notice Claim _account cumulative rewards
     * @dev Reward token will be transfer to the _account
     * @param _account Owner of the rewards
     * @return Amount of reward tokens transferred
     */
    function claim(address _account) external returns (uint256);

    /**
     * @notice Return _account claimable rewards
     * @dev No reward token are transferred
     * @param _account Owner of the rewards
     * @return Amount of reward tokens that can be claim
     */
    function claimable(address _account) external view returns (uint256);

    /**
     * @notice Return _account staked amount
     * @param _account Owner of the staking
     * @return Staked amount
     */
    function stakedAmount(address _account) external view returns (uint256);

    /**
     * @notice Update global cumulative reward
     * @dev No reward token are transferred
     */
    function updateRewards() external;

    /**
     * @notice Deposit rewards
     * @dev Transfer from called here
     * @param _rewards Amount of reward asset transferer
     */
    function depositRewards(uint256 _rewards) external;

    error AddressCannotBeZeroAddress();
    error AmountCannotBeZero();
    error AmountExceedsStakedAmount();
}

File 43 of 51 : IJonesGlpVaultRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IJonesGlpVaultRouter {
    function depositGlp(uint256 _assets, address _sender, bool _compound) external returns (uint256);
    function depositStable(uint256 _assets, bool _compound, address _user) external returns (uint256);
    function redeemGlpAdapter(uint256 _shares, bool _compound, address _token, address _user, bool _native)
        external
        returns (uint256);
}

File 44 of 51 : IJonesUsdVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {IAggregatorV3} from "./IAggregatorV3.sol";

interface IJonesUsdVault {
    function priceOracle() external view returns (IAggregatorV3);
    function tvl() external view returns (uint256);
}

File 45 of 51 : IRewardTracker.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

interface IRewardTracker {
    function depositBalances(address _account, address _depositToken) external view returns (uint256);
    function stakedAmounts(address _account) external view returns (uint256);
    function updateRewards() external;
    function stake(address _depositToken, uint256 _amount) external;
    function stakeForAccount(address _fundingAccount, address _account, address _depositToken, uint256 _amount)
        external;
    function unstake(address _depositToken, uint256 _amount) external;
    function unstakeForAccount(address _account, address _depositToken, uint256 _amount, address _receiver) external;
    function tokensPerInterval() external view returns (uint256);
    function claim(address _receiver) external returns (uint256);
    function claimForAccount(address _account, address _receiver) external returns (uint256);
    function claimable(address _account) external view returns (uint256);
    function averageStakedAmounts(address _account) external view returns (uint256);
    function cumulativeRewards(address _account) external view returns (uint256);
}

File 46 of 51 : IStakedGlp.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IStakedGlp {
    function stakedGlpTracker() external view returns (address);
}

File 47 of 51 : IWhitelistController.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IWhitelistController {
    struct RoleInfo {
        bool jGLP_BYPASS_CAP;
        bool jUSDC_BYPASS_TIME;
        uint256 jGLP_RETENTION;
        uint256 jUSDC_RETENTION;
    }

    function isInternalContract(address _account) external view returns (bool);
    function hasRole(bytes32 role, address account) external view returns (bool);
    function getUserRole(address _user) external view returns (bytes32);
    function getRoleInfo(bytes32 _role) external view returns (IWhitelistController.RoleInfo memory);
    function getDefaultRole() external view returns (IWhitelistController.RoleInfo memory);
    function isWhitelistedContract(address _account) external view returns (bool);
    function addToInternalContract(address _account) external;
    function addToWhitelistContracts(address _account) external;
    function removeFromInternalContract(address _account) external;
    function removeFromWhitelistContract(address _account) external;
    function bulkAddToWhitelistContracts(address[] calldata _accounts) external;
    function bulkRemoveFromWhitelistContract(address[] calldata _accounts) external;
}

File 48 of 51 : JonesBorrowableVault.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 Jones DAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol";
import {ERC4626} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {JonesGovernableVault} from "./JonesGovernableVault.sol";
import {IJonesBorrowableVault} from "../interfaces/IJonesBorrowableVault.sol";
import {Pausable} from "../common/Pausable.sol";

abstract contract JonesBorrowableVault is JonesGovernableVault, ERC4626, IJonesBorrowableVault, Pausable {
    bytes32 public constant BORROWER = bytes32("BORROWER");

    modifier onlyBorrower() {
        if (!hasRole(BORROWER, msg.sender)) {
            revert CallerIsNotBorrower();
        }
        _;
    }

    function addBorrower(address _newBorrower) external onlyGovernor {
        _grantRole(BORROWER, _newBorrower);

        emit BorrowerAdded(_newBorrower);
    }

    function removeBorrower(address _borrower) external onlyGovernor {
        _revokeRole(BORROWER, _borrower);

        emit BorrowerRemoved(_borrower);
    }

    function togglePause() external onlyGovernor {
        if (paused()) {
            _unpause();
            return;
        }

        _pause();
    }

    function borrow(uint256 _amount) external virtual onlyBorrower whenNotPaused returns (uint256) {
        IERC20(asset()).transfer(msg.sender, _amount);

        emit AssetsBorrowed(msg.sender, _amount);

        return _amount;
    }

    function repay(uint256 _amount) external virtual onlyBorrower returns (uint256) {
        IERC20(asset()).transferFrom(msg.sender, address(this), _amount);

        emit AssetsRepayed(msg.sender, _amount);

        return _amount;
    }

    event BorrowerAdded(address _newBorrower);
    event BorrowerRemoved(address _borrower);
    event AssetsBorrowed(address _borrower, uint256 _amount);
    event AssetsRepayed(address _borrower, uint256 _amount);

    error CallerIsNotBorrower();
}

File 49 of 51 : JonesGovernableVault.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 Jones DAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol";

abstract contract JonesGovernableVault is AccessControl {
    bytes32 public constant GOVERNOR = bytes32("GOVERNOR");

    constructor(address _governor) {
        _grantRole(GOVERNOR, _governor);
    }

    modifier onlyGovernor() {
        if (!hasRole(GOVERNOR, msg.sender)) {
            revert CallerIsNotGovernor();
        }
        _;
    }

    function updateGovernor(address _newGovernor) external onlyGovernor {
        _revokeRole(GOVERNOR, msg.sender);
        _grantRole(GOVERNOR, _newGovernor);

        emit GovernorUpdated(msg.sender, _newGovernor);
    }

    event GovernorUpdated(address _oldGovernor, address _newGovernor);

    error CallerIsNotGovernor();
}

File 50 of 51 : JonesOperableVault.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 Jones DAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {JonesGovernableVault} from "./JonesGovernableVault.sol";
import {ERC4626} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol";

abstract contract JonesOperableVault is JonesGovernableVault, ERC4626 {
    bytes32 public constant OPERATOR = bytes32("OPERATOR");

    modifier onlyOperator() {
        if (!hasRole(OPERATOR, msg.sender)) {
            revert CallerIsNotOperator();
        }

        _;
    }

    function addOperator(address _newOperator) external onlyGovernor {
        _grantRole(OPERATOR, _newOperator);

        emit OperatorAdded(_newOperator);
    }

    function removeOperator(address _operator) external onlyGovernor {
        _revokeRole(OPERATOR, _operator);

        emit OperatorRemoved(_operator);
    }

    function deposit(uint256 _assets, address _receiver) public virtual override onlyOperator returns (uint256) {
        return super.deposit(_assets, _receiver);
    }

    function mint(uint256 _shares, address _receiver) public virtual override onlyOperator returns (uint256) {
        return super.mint(_shares, _receiver);
    }

    function withdraw(uint256 _assets, address _receiver, address _owner)
        public
        virtual
        override
        onlyOperator
        returns (uint256)
    {
        return super.withdraw(_assets, _receiver, _owner);
    }

    function redeem(uint256 _shares, address _receiver, address _owner)
        public
        virtual
        override
        onlyOperator
        returns (uint256)
    {
        return super.redeem(_shares, _receiver, _owner);
    }

    event OperatorAdded(address _newOperator);
    event OperatorRemoved(address _operator);

    error CallerIsNotOperator();
}

File 51 of 51 : JonesUsdVault.sol
// SPDX-License-Identifier: UNLICENSED

// Copyright (c) 2023 Jones DAO - All rights reserved
// Jones DAO: https://www.jonesdao.io/

pragma solidity ^0.8.10;

import {ERC4626} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {JonesGovernableVault} from "./JonesGovernableVault.sol";
import {IAggregatorV3} from "../interfaces/IAggregatorV3.sol";
import {IJonesUsdVault} from "../interfaces/IJonesUsdVault.sol";

abstract contract JonesUsdVault is JonesGovernableVault, ERC4626, IJonesUsdVault {
    IAggregatorV3 public priceOracle;

    constructor(IAggregatorV3 _priceOracle) {
        priceOracle = _priceOracle;
    }

    function setPriceAggregator(IAggregatorV3 _newPriceOracle) external onlyGovernor {
        emit PriceOracleUpdated(address(priceOracle), address(_newPriceOracle));

        priceOracle = _newPriceOracle;
    }

    function tvl() external view returns (uint256) {
        return _toUsdValue(totalAssets());
    }

    function _toUsdValue(uint256 _value) internal view returns (uint256) {
        IAggregatorV3 oracle = priceOracle;

        (, int256 currentPrice,,,) = oracle.latestRoundData();

        uint8 totalDecimals = IERC20Metadata(asset()).decimals() + oracle.decimals();
        uint8 targetDecimals = 18;

        return totalDecimals > targetDecimals
            ? (_value * uint256(currentPrice)) / 10 ** (totalDecimals - targetDecimals)
            : (_value * uint256(currentPrice)) * 10 ** (targetDecimals - totalDecimals);
    }

    event PriceOracleUpdated(address _oldPriceOracle, address _newPriceOracle);
}

Settings
{
  "remappings": [
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solmate/=lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {
    "src/adapters/Curve2PoolAdapter.sol": {
      "Curve2PoolAdapter": "0xaE026E73a9F802fc2d26f3A5E394a22A00189505"
    },
    "src/adapters/GmxAdapter.sol": {
      "GmxAdapter": "0x405775bE91Aab1D06a3aFBB5A1a6D2A5cf7F5D2a"
    },
    "src/adapters/SsovAdapter.sol": {
      "SsovAdapter": "0xaa6348BfedFa57Ce6DB54529074905897B3d5D47"
    },
    "src/adapters/SushiAdapter.sol": {
      "SushiAdapter": "0xf9D9f8d9141A59b5E6AD3A88f35b5010EDad71A2"
    },
    "src/libraries/LPStrategyLib.sol": {
      "LPStrategyLib": "0xb330A28C8518c4095C8411191120D1739C2d8f60"
    },
    "src/libraries/OneInchZapLib.sol": {
      "OneInchZapLib": "0x46be03489b730EA8a38E4383FA2AAbC0C9419fA5"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"_stableRetentionPercentage","type":"uint256"},{"internalType":"uint256","name":"_glpRetentionPercentage","type":"uint256"},{"internalType":"contract IIncentiveReceiver","name":"_incentiveReceiver","type":"address"},{"internalType":"contract IJonesGlpRewardTracker","name":"_tracker","type":"address"},{"internalType":"contract GlpJonesRewards","name":"_jonesRewards","type":"address"},{"internalType":"contract IERC20","name":"_asset","type":"address"},{"internalType":"contract IERC20Metadata","name":"_vaultToken","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressCannotBeZeroAddress","type":"error"},{"inputs":[],"name":"AmountCannotBeZero","type":"error"},{"inputs":[],"name":"AmountExceedsStakedAmount","type":"error"},{"inputs":[],"name":"CallerIsNotAllowed","type":"error"},{"inputs":[],"name":"CallerIsNotGovernor","type":"error"},{"inputs":[],"name":"CallerIsNotKeeper","type":"error"},{"inputs":[],"name":"CallerIsNotOperator","type":"error"},{"inputs":[],"name":"RetentionPercentageOutOfRange","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_rewards","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_totalAssets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_retentions","type":"uint256"}],"name":"Compound","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"_assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldGovernor","type":"address"},{"indexed":false,"internalType":"address","name":"_newGovernor","type":"address"}],"name":"GovernorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newKeeper","type":"address"}],"name":"KeeperAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_operator","type":"address"}],"name":"KeeperRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newOperator","type":"address"}],"name":"OperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_operator","type":"address"}],"name":"OperatorRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"_assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"BASIS_POINTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOVERNOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KEEPER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newKeeper","type":"address"}],"name":"addKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOperator","type":"address"}],"name":"addOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"compound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"emergencyGlpWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"emergencyStableWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"glp","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"glpRetentionPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gmxRouter","outputs":[{"internalType":"contract IGmxRewardRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"incentiveReceiver","outputs":[{"internalType":"contract IIncentiveReceiver","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"jonesRewards","outputs":[{"internalType":"contract GlpJonesRewards","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"receiptBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"removeKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"removeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"contract JonesGlpVaultRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"_asset","type":"address"}],"name":"setAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IGmxRewardRouter","name":"_gmxRouter","type":"address"}],"name":"setGmxRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IIncentiveReceiver","name":"_incentiveReceiver","type":"address"}],"name":"setIncentiveReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract GlpJonesRewards","name":"_jonesRewards","type":"address"}],"name":"setJonesRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stableRetentionPercentage","type":"uint256"},{"internalType":"uint256","name":"_glpRetentionPercentage","type":"uint256"}],"name":"setNewRetentions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IJonesGlpRewardTracker","name":"_tracker","type":"address"}],"name":"setRewardTracker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract JonesGlpVaultRouter","name":"_router","type":"address"}],"name":"setRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"_vaultToken","type":"address"}],"name":"setVaultToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stableRetentionPercentage","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":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssetsDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"totalAssetsToDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tracker","outputs":[{"internalType":"contract IJonesGlpRewardTracker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newGovernor","type":"address"}],"name":"updateGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdc","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultToken","outputs":[{"internalType":"contract IERC20Metadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

6080604052600780546001600160a01b03191673b95db5b167d75e6d04227cfffa61069348d271f51790553480156200003757600080fd5b5060405162003681380380620036818339810160408190526200005a91620003b4565b33828281600390805190602001906200007592919062000216565b5080516200008b90600490602084019062000216565b505050620000ab6723a7ab22a92727a960c11b826200017160201b60201c565b50600160065564e8d4a51000891115620000d85760405163c33d13b360e01b815260040160405180910390fd5b64e8d4a51000881115620000ff5760405163c33d13b360e01b815260040160405180910390fd5b5050600a96909655600b94909455601180546001600160a01b03199081166001600160a01b0395861617909155601280548216928516929092179091556008805482169484169490941790935560098054841694831694909417909355601080549092169216919091179055620004d2565b60008281526005602090815260408083206001600160a01b038516845290915290205460ff16620002125760008281526005602090815260408083206001600160a01b03851684529091529020805460ff19166001179055620001d13390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b828054620002249062000495565b90600052602060002090601f01602090048101928262000248576000855562000293565b82601f106200026357805160ff191683800117855562000293565b8280016001018555821562000293579182015b828111156200029357825182559160200191906001019062000276565b50620002a1929150620002a5565b5090565b5b80821115620002a15760008155600101620002a6565b6001600160a01b0381168114620002d257600080fd5b50565b8051620002e281620002bc565b919050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200030f57600080fd5b81516001600160401b03808211156200032c576200032c620002e7565b604051601f8301601f19908116603f01168101908282118183101715620003575762000357620002e7565b816040528381526020925086838588010111156200037457600080fd5b600091505b8382101562000398578582018301518183018401529082019062000379565b83821115620003aa5760008385830101525b9695505050505050565b60008060008060008060008060006101208a8c031215620003d457600080fd5b8951985060208a0151975060408a0151620003ef81620002bc565b60608b01519097506200040281620002bc565b95506200041260808b01620002d5565b94506200042260a08b01620002d5565b93506200043260c08b01620002d5565b60e08b01519093506001600160401b03808211156200045057600080fd5b6200045e8d838e01620002fd565b93506101008c01519150808211156200047657600080fd5b50620004858c828d01620002fd565b9150509295985092959850929598565b600181811c90821680620004aa57607f821691505b60208210811415620004cc57634e487b7160e01b600052602260045260246000fd5b50919050565b61319f80620004e26000396000f3fe608060405234801561001057600080fd5b506004361061038e5760003560e01c806376bbc90d116101de578063c0d786551161010f578063ef8b30f7116100ad578063f69e20461161007c578063f69e2046146107ba578063f6af1602146107c2578063f887ea40146107d5578063fa43be14146107e857600080fd5b8063ef8b30f714610778578063f2293bb01461078b578063f29d0aca1461079e578063f52bccad146107a757600080fd5b8063d0d552dd116100e9578063d0d552dd14610733578063d547741f14610746578063dd62ed3e14610759578063e1f1c4a71461076c57600080fd5b8063c0d78655146106fa578063c4ade30b1461070d578063c6fee6c41461072057600080fd5b80639870d7fe1161017c578063a457c2d711610156578063a457c2d7146106b8578063a546c220146106cb578063a9059cbb146106d4578063ac8a584a146106e757600080fd5b80639870d7fe1461068a5780639abb810d1461069d578063a217fddf146106b057600080fd5b8063862a179e116101b8578063862a179e1461064d57806391d148541461065d57806395d89b4114610670578063983d27371461067857600080fd5b806376bbc90d1461060c57806378a207ee1461061f5780637bde82f21461063a57600080fd5b806338d52e0f116102c3578063522131da116102615780636dc0ae22116102305780636dc0ae22146105c15780636e553f65146105d3578063703bafd5146105e657806370a08231146105f957600080fd5b8063522131da146105755780635f04591d1461058857806362f384ad1461059b578063634e3180146105ae57600080fd5b80633e74a43f1161029d5780633e74a43f146105215780633fc8cef3146105345780634032b72b1461054f5780634cdad5061461056257600080fd5b806338d52e0f146104c857806339509351146104f35780633e413bee1461050657600080fd5b8063248a9ca3116103305780632f4e3e9b1161030a5780632f4e3e9b14610473578063313ce5671461048657806331abae3d1461049557806336568abe146104b557600080fd5b8063248a9ca31461042a57806328441ca81461044d5780632f2ff15d1461046057600080fd5b8063095ea7b31161036c578063095ea7b3146103e757806314ae9f2e146103fa57806318160ddd1461040f57806323b872dd1461041757600080fd5b806301e1d1141461039357806301ffc9a7146103af57806306fdde03146103d2575b600080fd5b61039c600c5481565b6040519081526020015b60405180910390f35b6103c26103bd366004612c4e565b6107f1565b60405190151581526020016103a6565b6103da610828565b6040516103a69190612ca4565b6103c26103f5366004612cec565b610837565b61040d610408366004612d18565b61084a565b005b61039c6108a5565b6103c2610425366004612d35565b6108b0565b61039c610438366004612d76565b60009081526005602052604090206001015490565b61040d61045b366004612d18565b6108d0565b61040d61046e366004612d8f565b6108fa565b61040d610481366004612dbf565b610924565b604051601281526020016103a6565b61039c6104a3366004612d18565b600e6020526000908152604090205481565b61040d6104c3366004612d8f565b610983565b6008546104db906001600160a01b031681565b6040516001600160a01b0390911681526020016103a6565b6103c2610501366004612cec565b610a06565b6104db73ff970a61a04b1ca14834a43f5de4533ebddb5cc881565b61039c61052f366004612cec565b610a32565b6104db7382af49447d8a07e3bd95bd0d56f35241523fbab181565b61040d61055d366004612d18565b610a71565b61039c610570366004612d76565b610ac5565b6012546104db906001600160a01b031681565b61040d610596366004612d18565b610ad2565b61040d6105a9366004612d18565b610afc565b6007546104db906001600160a01b031681565b61039c6723a7ab22a92727a960c11b81565b61039c6105e1366004612d8f565b610b6c565b6009546104db906001600160a01b031681565b61039c610607366004612d18565b610bcc565b61040d61061a366004612d18565b610bea565b6104db735402b5f40310bded796c7d0f3ff6683f5c0cffdf81565b61039c610648366004612d8f565b610c14565b61039c6525a2a2a822a960d11b81565b6103c261066b366004612d8f565b610c68565b6103da610c93565b61039c6727a822a920aa27a960c11b81565b61040d610698366004612d18565b610c9d565b61040d6106ab366004612d18565b610cf3565b61039c600081565b6103c26106c6366004612cec565b610ebb565b61039c600d5481565b6103c26106e2366004612cec565b610f41565b61040d6106f5366004612d18565b610f58565b61040d610708366004612d18565b610fae565b6011546104db906001600160a01b031681565b61040d61072e366004612d18565b610fd8565b61040d610741366004612d18565b611002565b61040d610754366004612d8f565b61102c565b61039c610767366004612de1565b611051565b61039c64e8d4a5100081565b61039c610786366004612d76565b61107e565b61040d610799366004612d18565b61108b565b61039c600a5481565b6010546104db906001600160a01b031681565b61040d6110b5565b61040d6107d0366004612d18565b61110a565b600f546104db906001600160a01b031681565b61039c600b5481565b60006001600160e01b03198216637965db0b60e01b148061082257506301ffc9a760e01b6001600160e01b03198316145b92915050565b6060610832611155565b905090565b600061084383836111e7565b9392505050565b6108526111f5565b6108656525a2a2a822a960d11b82611227565b6040516001600160a01b03821681527fa7a775c2c8141f7985c111748ec31c11e5e44b83528e105c8d1d4e8e6b81cf80906020015b60405180910390a150565b600061083260025490565b60006108bd84848461128e565b6108c884848461147b565b949350505050565b6108d86111f5565b600980546001600160a01b0319166001600160a01b0392909216919091179055565b60008281526005602052604090206001015461091581611494565b61091f83836114a1565b505050565b61092c6111f5565b64e8d4a510008211156109525760405163c33d13b360e01b815260040160405180910390fd5b64e8d4a510008111156109785760405163c33d13b360e01b815260040160405180910390fd5b600a91909155600b55565b6001600160a01b03811633146109f85760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b610a028282611227565b5050565b600033610a28818585610a198383611051565b610a239190612e25565b611527565b5060019392505050565b600080610a48610a4185610bcc565b600061164c565b6001600160a01b0385166000908152600e60205260408120549192506108c89185918490611701565b610a796111f5565b610a8c6525a2a2a822a960d11b826114a1565b6040516001600160a01b03821681527f1584773458d98c71b34a270ee1100b3a42889bf91e3b7a858563b684c24d838e9060200161089a565b600061082282600061164c565b610ada6111f5565b601280546001600160a01b0319166001600160a01b0392909216919091179055565b610b046111f5565b610b196723a7ab22a92727a960c11b33611227565b610b2e6723a7ab22a92727a960c11b826114a1565b604080513381526001600160a01b03831660208201527f5af6a85e864342d4f108c43dd574d98480c91f1de0ac2a9f66d826dee49bd9bb910161089a565b6000610b7661175e565b610b8b6727a822a920aa27a960c11b33610c68565b610ba85760405163683b4ec760e11b815260040160405180910390fd5b6000610bb38461107e565b9050610bc08385836117b8565b90506108226001600655565b6001600160a01b038116600090815260208190526040812054610822565b610bf26111f5565b600780546001600160a01b0319166001600160a01b0392909216919091179055565b6000610c1e61175e565b610c336727a822a920aa27a960c11b33610c68565b610c505760405163683b4ec760e11b815260040160405180910390fd5b6000610c5b84610ac5565b9050610bc0838286611a2b565b60009182526005602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6060610832611cb2565b610ca56111f5565b610cba6727a822a920aa27a960c11b826114a1565b6040516001600160a01b03821681527fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d9060200161089a565b610cfb6111f5565b610d03611cc1565b600f5460105460405163f993185560e01b81523060048201526001600160a01b0392831692639cf295ae92169063f9931855906024015b602060405180830381865afa158015610d57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7b9190612e3d565b6040516001600160e01b031960e084901b1681526004810191909152600060248201526044016020604051808303816000875af1158015610dc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de49190612e3d565b506008546040516370a0823160e01b81523060048201526001600160a01b039091169063a9059cbb90839083906370a0823190602401602060405180830381865afa158015610e37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5b9190612e3d565b6040518363ffffffff1660e01b8152600401610e78929190612e56565b6020604051808303816000875af1158015610e97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a029190612e6f565b60003381610ec98286611051565b905083811015610f295760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016109ef565b610f368286868403611527565b506001949350505050565b6000610f4e33848461128e565b61084383836124ab565b610f606111f5565b610f756727a822a920aa27a960c11b82611227565b6040516001600160a01b03821681527f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d9060200161089a565b610fb66111f5565b600f80546001600160a01b0319166001600160a01b0392909216919091179055565b610fe06111f5565b601180546001600160a01b0319166001600160a01b0392909216919091179055565b61100a6111f5565b600880546001600160a01b0319166001600160a01b0392909216919091179055565b60008281526005602052604090206001015461104781611494565b61091f8383611227565b6001600160a01b038083166000908152600160209081526040808320938516835292905290812054610843565b60006108228260006124b9565b6110936111f5565b601080546001600160a01b0319166001600160a01b0392909216919091179055565b6110ca6727a822a920aa27a960c11b33610c68565b806110e357506110e36525a2a2a822a960d11b33610c68565b6111005760405163b8762e7760e01b815260040160405180910390fd5b611108611cc1565b565b6111126111f5565b61111a611cc1565b600f5460105460405163f993185560e01b81523060048201526001600160a01b0392831692635e13086a92169063f993185590602401610d3a565b60606003805461116490612e91565b80601f016020809104026020016040519081016040528092919081815260200182805461119090612e91565b80156111dd5780601f106111b2576101008083540402835291602001916111dd565b820191906000526020600020905b8154815290600101906020018083116111c057829003601f168201915b5050505050905090565b600033610a28818585611527565b61120a6723a7ab22a92727a960c11b33610c68565b611108576040516303fa15f960e11b815260040160405180910390fd5b6112318282610c68565b15610a025760008281526005602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b600061129982610ac5565b905060006112a78583610a32565b6001600160a01b0386166000908152600e60205260409020549091506112ce908290612ecc565b6001600160a01b038087166000908152600e602052604080822093909355908616815220546112fe908290612e25565b6001600160a01b038086166000908152600e60205260409020919091556008541673ff970a61a04b1ca14834a43f5de4533ebddb5cc8141561147457601254604051630c00007b60e41b81526001600160a01b0387811660048301529091169063c00007b0906024016020604051808303816000875af1158015611386573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113aa9190612e3d565b5060125460405163f3fef3a360e01b81526001600160a01b039091169063f3fef3a3906113dd9088908590600401612e56565b600060405180830381600087803b1580156113f757600080fd5b505af115801561140b573d6000803e3d6000fd5b50506012546040516356e4bb9760e11b81526001600160a01b03909116925063adc9772e91506114419087908590600401612e56565b600060405180830381600087803b15801561145b57600080fd5b505af115801561146f573d6000803e3d6000fd5b505050505b5050505050565b60003361148985828561256c565b610f368585856125e6565b61149e813361278a565b50565b6114ab8282610c68565b610a025760008281526005602090815260408083206001600160a01b03851684529091529020805460ff191660011790556114e33390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6001600160a01b0383166115895760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016109ef565b6001600160a01b0382166115ea5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016109ef565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000806116576108a5565b9050801561167457600c5461166f9085908386611701565b6108c8565b6009546040805163313ce56760e01b815290516108c8926001600160a01b03169163313ce5679160048083019260209291908290030181865afa1580156116bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e39190612ee3565b6116ee90600a612fea565b60125b6116fc90600a612fea565b869190865b60008061170f8686866127e3565b9050600183600281111561172557611725612ff9565b14801561174257506000848061173d5761173d61300f565b868809115b1561175557611752600182612e25565b90505b95945050505050565b600260065414156117b15760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109ef565b6002600655565b6009546040516323b872dd60e01b8152336004820152306024820152604481018490526001600160a01b03909116906323b872dd906064016020604051808303816000875af115801561180f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118339190612e6f565b506001600160a01b0383166000908152600e6020526040902054611858908390612e25565b6001600160a01b038085166000908152600e60205260409081902092909255600954601054925163095ea7b360e01b81529082169263095ea7b3926118a4929116908690600401612e56565b6020604051808303816000875af11580156118c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e79190612e6f565b506010546040516356e4bb9760e11b81526001600160a01b039091169063adc9772e9061191a9030908690600401612e56565b6020604051808303816000875af1158015611939573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061195d9190612e3d565b5081600d5461196c9190612e25565b600d5560105460405163f993185560e01b81523060048201526001600160a01b039091169063f993185590602401602060405180830381865afa1580156119b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119db9190612e3d565b600c556119e88382612893565b60408051838152602081018390526001600160a01b0385169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910161163f565b6000611a378484610a32565b9050611a438483612952565b6001600160a01b0384166000908152600e6020526040902054611a67908290612ecc565b6001600160a01b0385166000908152600e6020526040902055600d54611a8e908290612ecc565b600d5560105460405163f3fef3a360e01b81526001600160a01b039091169063f3fef3a390611ac39030908790600401612e56565b6020604051808303816000875af1158015611ae2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b069190612e3d565b5060095460105460405163095ea7b360e01b81526001600160a01b039283169263095ea7b392611b3d929116908790600401612e56565b6020604051808303816000875af1158015611b5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b809190612e6f565b506010546040516356e4bb9760e11b81526001600160a01b039091169063adc9772e90611bb39087908790600401612e56565b6020604051808303816000875af1158015611bd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bf69190612e3d565b5060105460405163f993185560e01b81523060048201526001600160a01b039091169063f993185590602401602060405180830381865afa158015611c3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c639190612e3d565b600c5560408051848152602081018490526001600160a01b0386169133917ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567910160405180910390a350505050565b60606004805461116490612e91565b600080600f60009054906101000a90046001600160a01b03166001600160a01b031663372500ab6040518163ffffffff1660e01b81526004016060604051808303816000875af1158015611d19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d3d9190613025565b50909250905080156121c4576000611d5782600b54612a84565b90508015611e6e5760115460405163095ea7b360e01b81527382af49447d8a07e3bd95bd0d56f35241523fbab19163095ea7b391611da3916001600160a01b0316908590600401612e56565b6020604051808303816000875af1158015611dc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de69190612e6f565b506011546040516311f9fbc960e21b81526001600160a01b03909116906347e7ef2490611e2d907382af49447d8a07e3bd95bd0d56f35241523fbab1908590600401612e56565b600060405180830381600087803b158015611e4757600080fd5b505af1158015611e5b573d6000803e3d6000fd5b505050508082611e6b9190612ecc565b91505b7382af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031663095ea7b3600760009054906101000a90046001600160a01b03166001600160a01b031663fa6db1bc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ee4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f089190613053565b846040518363ffffffff1660e01b8152600401611f26929190612e56565b6020604051808303816000875af1158015611f45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f699190612e6f565b5060075460405163364e231160e01b81527382af49447d8a07e3bd95bd0d56f35241523fbab160048201526024810184905260006044820181905260648201819052916001600160a01b03169063364e2311906084016020604051808303816000875af1158015611fde573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120029190612e3d565b600f5460405163095ea7b360e01b8152919450849250735402b5f40310bded796c7d0f3ff6683f5c0cffdf9163095ea7b39161204e916001600160a01b03909116908590600401612e56565b6020604051808303816000875af115801561206d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120919190612e6f565b50600f54604051634b84512360e01b815260048101859052306024820152600060448201526001600160a01b0390911690634b845123906064016020604051808303816000875af11580156120ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061210e9190612e3d565b5060105460405163f993185560e01b81523060048201526001600160a01b039091169063f993185590602401602060405180830381865afa158015612157573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061217b9190612e3d565b600c81905560408051858152602081019290925281018390527f27aaed1e2ace6cfa5a07a3dc6557aed2566349b8a63e8bc208943164c6bb615f9060600160405180910390a150505b8115610a025760006121d883600a54612a84565b905080156122ef5760115460405163095ea7b360e01b815273ff970a61a04b1ca14834a43f5de4533ebddb5cc89163095ea7b391612224916001600160a01b0316908590600401612e56565b6020604051808303816000875af1158015612243573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122679190612e6f565b506011546040516311f9fbc960e21b81526001600160a01b03909116906347e7ef24906122ae9073ff970a61a04b1ca14834a43f5de4533ebddb5cc8908590600401612e56565b600060405180830381600087803b1580156122c857600080fd5b505af11580156122dc573d6000803e3d6000fd5b5050505080836122ec9190612ecc565b92505b600f5460405163095ea7b360e01b815273ff970a61a04b1ca14834a43f5de4533ebddb5cc89163095ea7b391612333916001600160a01b0316908790600401612e56565b6020604051808303816000875af1158015612352573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123769190612e6f565b50600f5460405163429fd52b60e11b815260048101859052600060248201523060448201526001600160a01b039091169063853faa56906064016020604051808303816000875af11580156123cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f39190612e3d565b5060105460405163f993185560e01b81523060048201526001600160a01b039091169063f993185590602401602060405180830381865afa15801561243c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124609190612e3d565b600c81905560408051858152602081019290925281018290527f27aaed1e2ace6cfa5a07a3dc6557aed2566349b8a63e8bc208943164c6bb615f9060600160405180910390a1505050565b600033610a288185856125e6565b6000806124c46108a5565b90508315806124d1575080155b6124e657600c5461166f908590839086611701565b6108c86124f56012600a612fea565b600960009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612548573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f19190612ee3565b60006125788484611051565b905060001981146125e057818110156125d35760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016109ef565b6125e08484848403611527565b50505050565b6001600160a01b03831661264a5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016109ef565b6001600160a01b0382166126ac5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016109ef565b6001600160a01b038316600090815260208190526040902054818110156127245760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016109ef565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36125e0565b6127948282610c68565b610a02576127a181612aa0565b6127ac836020612ab2565b6040516020016127bd929190613070565b60408051601f198184030181529082905262461bcd60e51b82526109ef91600401612ca4565b60008080600019858709858702925082811083820303915050806000141561281e578382816128145761281461300f565b0492505050610843565b80841161282a57600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6001600160a01b0382166128e95760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016109ef565b80600260008282546128fb9190612e25565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0382166129b25760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016109ef565b6001600160a01b03821660009081526020819052604090205481811015612a265760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016109ef565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b600064e8d4a51000612a9683856130e5565b6108439190613104565b60606108226001600160a01b03831660145b60606000612ac18360026130e5565b612acc906002612e25565b67ffffffffffffffff811115612ae457612ae4613126565b6040519080825280601f01601f191660200182016040528015612b0e576020820181803683370190505b509050600360fc1b81600081518110612b2957612b2961313c565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110612b5857612b5861313c565b60200101906001600160f81b031916908160001a9053506000612b7c8460026130e5565b612b87906001612e25565b90505b6001811115612bff576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110612bbb57612bbb61313c565b1a60f81b828281518110612bd157612bd161313c565b60200101906001600160f81b031916908160001a90535060049490941c93612bf881613152565b9050612b8a565b5083156108435760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016109ef565b600060208284031215612c6057600080fd5b81356001600160e01b03198116811461084357600080fd5b60005b83811015612c93578181015183820152602001612c7b565b838111156125e05750506000910152565b6020815260008251806020840152612cc3816040850160208701612c78565b601f01601f19169190910160400192915050565b6001600160a01b038116811461149e57600080fd5b60008060408385031215612cff57600080fd5b8235612d0a81612cd7565b946020939093013593505050565b600060208284031215612d2a57600080fd5b813561084381612cd7565b600080600060608486031215612d4a57600080fd5b8335612d5581612cd7565b92506020840135612d6581612cd7565b929592945050506040919091013590565b600060208284031215612d8857600080fd5b5035919050565b60008060408385031215612da257600080fd5b823591506020830135612db481612cd7565b809150509250929050565b60008060408385031215612dd257600080fd5b50508035926020909101359150565b60008060408385031215612df457600080fd5b8235612dff81612cd7565b91506020830135612db481612cd7565b634e487b7160e01b600052601160045260246000fd5b60008219821115612e3857612e38612e0f565b500190565b600060208284031215612e4f57600080fd5b5051919050565b6001600160a01b03929092168252602082015260400190565b600060208284031215612e8157600080fd5b8151801515811461084357600080fd5b600181811c90821680612ea557607f821691505b60208210811415612ec657634e487b7160e01b600052602260045260246000fd5b50919050565b600082821015612ede57612ede612e0f565b500390565b600060208284031215612ef557600080fd5b815160ff8116811461084357600080fd5b600181815b80851115612f41578160001904821115612f2757612f27612e0f565b80851615612f3457918102915b93841c9390800290612f0b565b509250929050565b600082612f5857506001610822565b81612f6557506000610822565b8160018114612f7b5760028114612f8557612fa1565b6001915050610822565b60ff841115612f9657612f96612e0f565b50506001821b610822565b5060208310610133831016604e8410600b8410161715612fc4575081810a610822565b612fce8383612f06565b8060001904821115612fe257612fe2612e0f565b029392505050565b600061084360ff841683612f49565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b60008060006060848603121561303a57600080fd5b8351925060208401519150604084015190509250925092565b60006020828403121561306557600080fd5b815161084381612cd7565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516130a8816017850160208801612c78565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516130d9816028840160208801612c78565b01602801949350505050565b60008160001904831182151516156130ff576130ff612e0f565b500290565b60008261312157634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161316157613161612e0f565b50600019019056fea264697066735822122031c7ac694c581e7a3354bb4b235023d5abde7b9066982ffc179ce6a2a7f335be64736f6c634300080a0033000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a817c8000000000000000000000000005a446ba4d4bf482a3e63648e76e9404e784f7bbc000000000000000000000000eb23c7e19db72f9a728fd64e1caa459e457cfaca0000000000000000000000003c5376bf032cec70c342b64f43778dc06bbd6673000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000a485a0bc44988b95245d5f20497ccaff58a73e9900000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000a4a6f6e657320555344430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056a55534443000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061038e5760003560e01c806376bbc90d116101de578063c0d786551161010f578063ef8b30f7116100ad578063f69e20461161007c578063f69e2046146107ba578063f6af1602146107c2578063f887ea40146107d5578063fa43be14146107e857600080fd5b8063ef8b30f714610778578063f2293bb01461078b578063f29d0aca1461079e578063f52bccad146107a757600080fd5b8063d0d552dd116100e9578063d0d552dd14610733578063d547741f14610746578063dd62ed3e14610759578063e1f1c4a71461076c57600080fd5b8063c0d78655146106fa578063c4ade30b1461070d578063c6fee6c41461072057600080fd5b80639870d7fe1161017c578063a457c2d711610156578063a457c2d7146106b8578063a546c220146106cb578063a9059cbb146106d4578063ac8a584a146106e757600080fd5b80639870d7fe1461068a5780639abb810d1461069d578063a217fddf146106b057600080fd5b8063862a179e116101b8578063862a179e1461064d57806391d148541461065d57806395d89b4114610670578063983d27371461067857600080fd5b806376bbc90d1461060c57806378a207ee1461061f5780637bde82f21461063a57600080fd5b806338d52e0f116102c3578063522131da116102615780636dc0ae22116102305780636dc0ae22146105c15780636e553f65146105d3578063703bafd5146105e657806370a08231146105f957600080fd5b8063522131da146105755780635f04591d1461058857806362f384ad1461059b578063634e3180146105ae57600080fd5b80633e74a43f1161029d5780633e74a43f146105215780633fc8cef3146105345780634032b72b1461054f5780634cdad5061461056257600080fd5b806338d52e0f146104c857806339509351146104f35780633e413bee1461050657600080fd5b8063248a9ca3116103305780632f4e3e9b1161030a5780632f4e3e9b14610473578063313ce5671461048657806331abae3d1461049557806336568abe146104b557600080fd5b8063248a9ca31461042a57806328441ca81461044d5780632f2ff15d1461046057600080fd5b8063095ea7b31161036c578063095ea7b3146103e757806314ae9f2e146103fa57806318160ddd1461040f57806323b872dd1461041757600080fd5b806301e1d1141461039357806301ffc9a7146103af57806306fdde03146103d2575b600080fd5b61039c600c5481565b6040519081526020015b60405180910390f35b6103c26103bd366004612c4e565b6107f1565b60405190151581526020016103a6565b6103da610828565b6040516103a69190612ca4565b6103c26103f5366004612cec565b610837565b61040d610408366004612d18565b61084a565b005b61039c6108a5565b6103c2610425366004612d35565b6108b0565b61039c610438366004612d76565b60009081526005602052604090206001015490565b61040d61045b366004612d18565b6108d0565b61040d61046e366004612d8f565b6108fa565b61040d610481366004612dbf565b610924565b604051601281526020016103a6565b61039c6104a3366004612d18565b600e6020526000908152604090205481565b61040d6104c3366004612d8f565b610983565b6008546104db906001600160a01b031681565b6040516001600160a01b0390911681526020016103a6565b6103c2610501366004612cec565b610a06565b6104db73ff970a61a04b1ca14834a43f5de4533ebddb5cc881565b61039c61052f366004612cec565b610a32565b6104db7382af49447d8a07e3bd95bd0d56f35241523fbab181565b61040d61055d366004612d18565b610a71565b61039c610570366004612d76565b610ac5565b6012546104db906001600160a01b031681565b61040d610596366004612d18565b610ad2565b61040d6105a9366004612d18565b610afc565b6007546104db906001600160a01b031681565b61039c6723a7ab22a92727a960c11b81565b61039c6105e1366004612d8f565b610b6c565b6009546104db906001600160a01b031681565b61039c610607366004612d18565b610bcc565b61040d61061a366004612d18565b610bea565b6104db735402b5f40310bded796c7d0f3ff6683f5c0cffdf81565b61039c610648366004612d8f565b610c14565b61039c6525a2a2a822a960d11b81565b6103c261066b366004612d8f565b610c68565b6103da610c93565b61039c6727a822a920aa27a960c11b81565b61040d610698366004612d18565b610c9d565b61040d6106ab366004612d18565b610cf3565b61039c600081565b6103c26106c6366004612cec565b610ebb565b61039c600d5481565b6103c26106e2366004612cec565b610f41565b61040d6106f5366004612d18565b610f58565b61040d610708366004612d18565b610fae565b6011546104db906001600160a01b031681565b61040d61072e366004612d18565b610fd8565b61040d610741366004612d18565b611002565b61040d610754366004612d8f565b61102c565b61039c610767366004612de1565b611051565b61039c64e8d4a5100081565b61039c610786366004612d76565b61107e565b61040d610799366004612d18565b61108b565b61039c600a5481565b6010546104db906001600160a01b031681565b61040d6110b5565b61040d6107d0366004612d18565b61110a565b600f546104db906001600160a01b031681565b61039c600b5481565b60006001600160e01b03198216637965db0b60e01b148061082257506301ffc9a760e01b6001600160e01b03198316145b92915050565b6060610832611155565b905090565b600061084383836111e7565b9392505050565b6108526111f5565b6108656525a2a2a822a960d11b82611227565b6040516001600160a01b03821681527fa7a775c2c8141f7985c111748ec31c11e5e44b83528e105c8d1d4e8e6b81cf80906020015b60405180910390a150565b600061083260025490565b60006108bd84848461128e565b6108c884848461147b565b949350505050565b6108d86111f5565b600980546001600160a01b0319166001600160a01b0392909216919091179055565b60008281526005602052604090206001015461091581611494565b61091f83836114a1565b505050565b61092c6111f5565b64e8d4a510008211156109525760405163c33d13b360e01b815260040160405180910390fd5b64e8d4a510008111156109785760405163c33d13b360e01b815260040160405180910390fd5b600a91909155600b55565b6001600160a01b03811633146109f85760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b610a028282611227565b5050565b600033610a28818585610a198383611051565b610a239190612e25565b611527565b5060019392505050565b600080610a48610a4185610bcc565b600061164c565b6001600160a01b0385166000908152600e60205260408120549192506108c89185918490611701565b610a796111f5565b610a8c6525a2a2a822a960d11b826114a1565b6040516001600160a01b03821681527f1584773458d98c71b34a270ee1100b3a42889bf91e3b7a858563b684c24d838e9060200161089a565b600061082282600061164c565b610ada6111f5565b601280546001600160a01b0319166001600160a01b0392909216919091179055565b610b046111f5565b610b196723a7ab22a92727a960c11b33611227565b610b2e6723a7ab22a92727a960c11b826114a1565b604080513381526001600160a01b03831660208201527f5af6a85e864342d4f108c43dd574d98480c91f1de0ac2a9f66d826dee49bd9bb910161089a565b6000610b7661175e565b610b8b6727a822a920aa27a960c11b33610c68565b610ba85760405163683b4ec760e11b815260040160405180910390fd5b6000610bb38461107e565b9050610bc08385836117b8565b90506108226001600655565b6001600160a01b038116600090815260208190526040812054610822565b610bf26111f5565b600780546001600160a01b0319166001600160a01b0392909216919091179055565b6000610c1e61175e565b610c336727a822a920aa27a960c11b33610c68565b610c505760405163683b4ec760e11b815260040160405180910390fd5b6000610c5b84610ac5565b9050610bc0838286611a2b565b60009182526005602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6060610832611cb2565b610ca56111f5565b610cba6727a822a920aa27a960c11b826114a1565b6040516001600160a01b03821681527fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d9060200161089a565b610cfb6111f5565b610d03611cc1565b600f5460105460405163f993185560e01b81523060048201526001600160a01b0392831692639cf295ae92169063f9931855906024015b602060405180830381865afa158015610d57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7b9190612e3d565b6040516001600160e01b031960e084901b1681526004810191909152600060248201526044016020604051808303816000875af1158015610dc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de49190612e3d565b506008546040516370a0823160e01b81523060048201526001600160a01b039091169063a9059cbb90839083906370a0823190602401602060405180830381865afa158015610e37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5b9190612e3d565b6040518363ffffffff1660e01b8152600401610e78929190612e56565b6020604051808303816000875af1158015610e97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a029190612e6f565b60003381610ec98286611051565b905083811015610f295760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016109ef565b610f368286868403611527565b506001949350505050565b6000610f4e33848461128e565b61084383836124ab565b610f606111f5565b610f756727a822a920aa27a960c11b82611227565b6040516001600160a01b03821681527f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d9060200161089a565b610fb66111f5565b600f80546001600160a01b0319166001600160a01b0392909216919091179055565b610fe06111f5565b601180546001600160a01b0319166001600160a01b0392909216919091179055565b61100a6111f5565b600880546001600160a01b0319166001600160a01b0392909216919091179055565b60008281526005602052604090206001015461104781611494565b61091f8383611227565b6001600160a01b038083166000908152600160209081526040808320938516835292905290812054610843565b60006108228260006124b9565b6110936111f5565b601080546001600160a01b0319166001600160a01b0392909216919091179055565b6110ca6727a822a920aa27a960c11b33610c68565b806110e357506110e36525a2a2a822a960d11b33610c68565b6111005760405163b8762e7760e01b815260040160405180910390fd5b611108611cc1565b565b6111126111f5565b61111a611cc1565b600f5460105460405163f993185560e01b81523060048201526001600160a01b0392831692635e13086a92169063f993185590602401610d3a565b60606003805461116490612e91565b80601f016020809104026020016040519081016040528092919081815260200182805461119090612e91565b80156111dd5780601f106111b2576101008083540402835291602001916111dd565b820191906000526020600020905b8154815290600101906020018083116111c057829003601f168201915b5050505050905090565b600033610a28818585611527565b61120a6723a7ab22a92727a960c11b33610c68565b611108576040516303fa15f960e11b815260040160405180910390fd5b6112318282610c68565b15610a025760008281526005602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b600061129982610ac5565b905060006112a78583610a32565b6001600160a01b0386166000908152600e60205260409020549091506112ce908290612ecc565b6001600160a01b038087166000908152600e602052604080822093909355908616815220546112fe908290612e25565b6001600160a01b038086166000908152600e60205260409020919091556008541673ff970a61a04b1ca14834a43f5de4533ebddb5cc8141561147457601254604051630c00007b60e41b81526001600160a01b0387811660048301529091169063c00007b0906024016020604051808303816000875af1158015611386573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113aa9190612e3d565b5060125460405163f3fef3a360e01b81526001600160a01b039091169063f3fef3a3906113dd9088908590600401612e56565b600060405180830381600087803b1580156113f757600080fd5b505af115801561140b573d6000803e3d6000fd5b50506012546040516356e4bb9760e11b81526001600160a01b03909116925063adc9772e91506114419087908590600401612e56565b600060405180830381600087803b15801561145b57600080fd5b505af115801561146f573d6000803e3d6000fd5b505050505b5050505050565b60003361148985828561256c565b610f368585856125e6565b61149e813361278a565b50565b6114ab8282610c68565b610a025760008281526005602090815260408083206001600160a01b03851684529091529020805460ff191660011790556114e33390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6001600160a01b0383166115895760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016109ef565b6001600160a01b0382166115ea5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016109ef565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000806116576108a5565b9050801561167457600c5461166f9085908386611701565b6108c8565b6009546040805163313ce56760e01b815290516108c8926001600160a01b03169163313ce5679160048083019260209291908290030181865afa1580156116bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e39190612ee3565b6116ee90600a612fea565b60125b6116fc90600a612fea565b869190865b60008061170f8686866127e3565b9050600183600281111561172557611725612ff9565b14801561174257506000848061173d5761173d61300f565b868809115b1561175557611752600182612e25565b90505b95945050505050565b600260065414156117b15760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109ef565b6002600655565b6009546040516323b872dd60e01b8152336004820152306024820152604481018490526001600160a01b03909116906323b872dd906064016020604051808303816000875af115801561180f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118339190612e6f565b506001600160a01b0383166000908152600e6020526040902054611858908390612e25565b6001600160a01b038085166000908152600e60205260409081902092909255600954601054925163095ea7b360e01b81529082169263095ea7b3926118a4929116908690600401612e56565b6020604051808303816000875af11580156118c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e79190612e6f565b506010546040516356e4bb9760e11b81526001600160a01b039091169063adc9772e9061191a9030908690600401612e56565b6020604051808303816000875af1158015611939573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061195d9190612e3d565b5081600d5461196c9190612e25565b600d5560105460405163f993185560e01b81523060048201526001600160a01b039091169063f993185590602401602060405180830381865afa1580156119b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119db9190612e3d565b600c556119e88382612893565b60408051838152602081018390526001600160a01b0385169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910161163f565b6000611a378484610a32565b9050611a438483612952565b6001600160a01b0384166000908152600e6020526040902054611a67908290612ecc565b6001600160a01b0385166000908152600e6020526040902055600d54611a8e908290612ecc565b600d5560105460405163f3fef3a360e01b81526001600160a01b039091169063f3fef3a390611ac39030908790600401612e56565b6020604051808303816000875af1158015611ae2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b069190612e3d565b5060095460105460405163095ea7b360e01b81526001600160a01b039283169263095ea7b392611b3d929116908790600401612e56565b6020604051808303816000875af1158015611b5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b809190612e6f565b506010546040516356e4bb9760e11b81526001600160a01b039091169063adc9772e90611bb39087908790600401612e56565b6020604051808303816000875af1158015611bd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bf69190612e3d565b5060105460405163f993185560e01b81523060048201526001600160a01b039091169063f993185590602401602060405180830381865afa158015611c3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c639190612e3d565b600c5560408051848152602081018490526001600160a01b0386169133917ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567910160405180910390a350505050565b60606004805461116490612e91565b600080600f60009054906101000a90046001600160a01b03166001600160a01b031663372500ab6040518163ffffffff1660e01b81526004016060604051808303816000875af1158015611d19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d3d9190613025565b50909250905080156121c4576000611d5782600b54612a84565b90508015611e6e5760115460405163095ea7b360e01b81527382af49447d8a07e3bd95bd0d56f35241523fbab19163095ea7b391611da3916001600160a01b0316908590600401612e56565b6020604051808303816000875af1158015611dc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de69190612e6f565b506011546040516311f9fbc960e21b81526001600160a01b03909116906347e7ef2490611e2d907382af49447d8a07e3bd95bd0d56f35241523fbab1908590600401612e56565b600060405180830381600087803b158015611e4757600080fd5b505af1158015611e5b573d6000803e3d6000fd5b505050508082611e6b9190612ecc565b91505b7382af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031663095ea7b3600760009054906101000a90046001600160a01b03166001600160a01b031663fa6db1bc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ee4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f089190613053565b846040518363ffffffff1660e01b8152600401611f26929190612e56565b6020604051808303816000875af1158015611f45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f699190612e6f565b5060075460405163364e231160e01b81527382af49447d8a07e3bd95bd0d56f35241523fbab160048201526024810184905260006044820181905260648201819052916001600160a01b03169063364e2311906084016020604051808303816000875af1158015611fde573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120029190612e3d565b600f5460405163095ea7b360e01b8152919450849250735402b5f40310bded796c7d0f3ff6683f5c0cffdf9163095ea7b39161204e916001600160a01b03909116908590600401612e56565b6020604051808303816000875af115801561206d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120919190612e6f565b50600f54604051634b84512360e01b815260048101859052306024820152600060448201526001600160a01b0390911690634b845123906064016020604051808303816000875af11580156120ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061210e9190612e3d565b5060105460405163f993185560e01b81523060048201526001600160a01b039091169063f993185590602401602060405180830381865afa158015612157573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061217b9190612e3d565b600c81905560408051858152602081019290925281018390527f27aaed1e2ace6cfa5a07a3dc6557aed2566349b8a63e8bc208943164c6bb615f9060600160405180910390a150505b8115610a025760006121d883600a54612a84565b905080156122ef5760115460405163095ea7b360e01b815273ff970a61a04b1ca14834a43f5de4533ebddb5cc89163095ea7b391612224916001600160a01b0316908590600401612e56565b6020604051808303816000875af1158015612243573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122679190612e6f565b506011546040516311f9fbc960e21b81526001600160a01b03909116906347e7ef24906122ae9073ff970a61a04b1ca14834a43f5de4533ebddb5cc8908590600401612e56565b600060405180830381600087803b1580156122c857600080fd5b505af11580156122dc573d6000803e3d6000fd5b5050505080836122ec9190612ecc565b92505b600f5460405163095ea7b360e01b815273ff970a61a04b1ca14834a43f5de4533ebddb5cc89163095ea7b391612333916001600160a01b0316908790600401612e56565b6020604051808303816000875af1158015612352573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123769190612e6f565b50600f5460405163429fd52b60e11b815260048101859052600060248201523060448201526001600160a01b039091169063853faa56906064016020604051808303816000875af11580156123cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f39190612e3d565b5060105460405163f993185560e01b81523060048201526001600160a01b039091169063f993185590602401602060405180830381865afa15801561243c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124609190612e3d565b600c81905560408051858152602081019290925281018290527f27aaed1e2ace6cfa5a07a3dc6557aed2566349b8a63e8bc208943164c6bb615f9060600160405180910390a1505050565b600033610a288185856125e6565b6000806124c46108a5565b90508315806124d1575080155b6124e657600c5461166f908590839086611701565b6108c86124f56012600a612fea565b600960009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612548573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f19190612ee3565b60006125788484611051565b905060001981146125e057818110156125d35760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016109ef565b6125e08484848403611527565b50505050565b6001600160a01b03831661264a5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016109ef565b6001600160a01b0382166126ac5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016109ef565b6001600160a01b038316600090815260208190526040902054818110156127245760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016109ef565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36125e0565b6127948282610c68565b610a02576127a181612aa0565b6127ac836020612ab2565b6040516020016127bd929190613070565b60408051601f198184030181529082905262461bcd60e51b82526109ef91600401612ca4565b60008080600019858709858702925082811083820303915050806000141561281e578382816128145761281461300f565b0492505050610843565b80841161282a57600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6001600160a01b0382166128e95760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016109ef565b80600260008282546128fb9190612e25565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0382166129b25760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016109ef565b6001600160a01b03821660009081526020819052604090205481811015612a265760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016109ef565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b600064e8d4a51000612a9683856130e5565b6108439190613104565b60606108226001600160a01b03831660145b60606000612ac18360026130e5565b612acc906002612e25565b67ffffffffffffffff811115612ae457612ae4613126565b6040519080825280601f01601f191660200182016040528015612b0e576020820181803683370190505b509050600360fc1b81600081518110612b2957612b2961313c565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110612b5857612b5861313c565b60200101906001600160f81b031916908160001a9053506000612b7c8460026130e5565b612b87906001612e25565b90505b6001811115612bff576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110612bbb57612bbb61313c565b1a60f81b828281518110612bd157612bd161313c565b60200101906001600160f81b031916908160001a90535060049490941c93612bf881613152565b9050612b8a565b5083156108435760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016109ef565b600060208284031215612c6057600080fd5b81356001600160e01b03198116811461084357600080fd5b60005b83811015612c93578181015183820152602001612c7b565b838111156125e05750506000910152565b6020815260008251806020840152612cc3816040850160208701612c78565b601f01601f19169190910160400192915050565b6001600160a01b038116811461149e57600080fd5b60008060408385031215612cff57600080fd5b8235612d0a81612cd7565b946020939093013593505050565b600060208284031215612d2a57600080fd5b813561084381612cd7565b600080600060608486031215612d4a57600080fd5b8335612d5581612cd7565b92506020840135612d6581612cd7565b929592945050506040919091013590565b600060208284031215612d8857600080fd5b5035919050565b60008060408385031215612da257600080fd5b823591506020830135612db481612cd7565b809150509250929050565b60008060408385031215612dd257600080fd5b50508035926020909101359150565b60008060408385031215612df457600080fd5b8235612dff81612cd7565b91506020830135612db481612cd7565b634e487b7160e01b600052601160045260246000fd5b60008219821115612e3857612e38612e0f565b500190565b600060208284031215612e4f57600080fd5b5051919050565b6001600160a01b03929092168252602082015260400190565b600060208284031215612e8157600080fd5b8151801515811461084357600080fd5b600181811c90821680612ea557607f821691505b60208210811415612ec657634e487b7160e01b600052602260045260246000fd5b50919050565b600082821015612ede57612ede612e0f565b500390565b600060208284031215612ef557600080fd5b815160ff8116811461084357600080fd5b600181815b80851115612f41578160001904821115612f2757612f27612e0f565b80851615612f3457918102915b93841c9390800290612f0b565b509250929050565b600082612f5857506001610822565b81612f6557506000610822565b8160018114612f7b5760028114612f8557612fa1565b6001915050610822565b60ff841115612f9657612f96612e0f565b50506001821b610822565b5060208310610133831016604e8410600b8410161715612fc4575081810a610822565b612fce8383612f06565b8060001904821115612fe257612fe2612e0f565b029392505050565b600061084360ff841683612f49565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b60008060006060848603121561303a57600080fd5b8351925060208401519150604084015190509250925092565b60006020828403121561306557600080fd5b815161084381612cd7565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516130a8816017850160208801612c78565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516130d9816028840160208801612c78565b01602801949350505050565b60008160001904831182151516156130ff576130ff612e0f565b500290565b60008261312157634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161316157613161612e0f565b50600019019056fea264697066735822122031c7ac694c581e7a3354bb4b235023d5abde7b9066982ffc179ce6a2a7f335be64736f6c634300080a0033

[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.