creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/StakedCPT.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.18;\n\nimport \"./StakedPT.sol\";\nimport \"./interfaces/ITokenWrapper.sol\";\nimport \"./interfaces/ICurveV2Pool.sol\";\n\n/**\n * @title StakedCPT\n * @dev StakedCPT is a contract that represents staked Curve LP (Liquidity Provider) tokens,\n * allowing users to stake their LP tokens to earn rewards in another token (cvxtoken).\n * This contract extends ERC4626, implements ReentrancyGuard, and is Owned.\n */\ncontract StakedCPT is StakedPT {\n    using SafeTransferLib for ERC20;\n\n    /// @notice Curve pool contract\n    ICurveV2Pool immutable pool;\n    /// @dev CRV address\n    address internal constant CRV = 0xD533a949740bb3306d119CC777fa900bA034cd52;\n    /// @dev Curve CRV/WETH pool for swapping rewards out\n    ICurveV2Pool internal constant crvPool = ICurveV2Pool(0x4eBdF703948ddCEA3B11f675B4D1Fba9d2414A14);\n    /// @dev Curve CVX/WETH pool for swapping rewards out\n    ICurveV2Pool internal constant cvxPool = ICurveV2Pool(0xB576491F1E6e5E62f1d8F26062Ee822B40B0E0d4);\n\n    /**\n     * @dev Constructor to initialize the StakedCPT contract.\n     * @param _lptoken Address of the LP token\n     * @param _cvxtoken convex deposit token(a 1:1 token representing an lp deposit)\n     * @param _booster Address of the booster contract\n     * @param _treasury Address of the treasury\n     * @param _owner Address of the contract owner\n     * @param _minLockDuration Minimum lock duration for staked LP tokens\n     * @param _weth Address of the Wrapped Ether (WETH) contract\n     * @param _pid Pool ID in the rewards pool contract\n     * @param _pool Pool address for Curve pool\n     */\n    constructor(\n        address _lptoken,\n        address _cvxtoken,\n        address _booster,\n        address _treasury,\n        address _owner,\n        uint256 _minLockDuration,\n        address _weth,\n        uint256 _pid,\n        address _pool\n    ) StakedPT(_lptoken, _cvxtoken, _booster, _treasury, _owner, _minLockDuration, _weth, _pid) {\n        pool = ICurveV2Pool(_pool);\n\n        weth.approve(address(mevEth), type(uint256).max);\n        mevEth.approve(_pool, type(uint256).max);\n    }\n\n    /**\n     * @dev Query amount out of LP token for amounts in.\n     * @param amounts Array of amounts of tokens to be provided.\n     * @return lptokenAmount Amount of lp to expect.\n     */\n    function cptOut(uint256[2] calldata amounts) external view returns (uint256 lptokenAmount) {\n        lptokenAmount = pool.calc_token_amount(amounts) - 1;\n    }\n\n    /**\n     * @dev Zap into the Curve pool by providing tokens and receiving staked LP tokens in return.\n     * @param amounts Array of amounts of tokens to be provided.\n     * @param receiver Address to receive the staked LP tokens.\n     * @param lptokenAmount Amount of lp to expect.\n     * @return shares Number of shares representing the staked LP tokens.\n     */\n    function zapCPT(\n        uint256[2] calldata amounts,\n        address receiver,\n        uint256 lptokenAmount\n    ) external payable nonReentrant returns (uint256 shares) {\n        address[2] memory tokens;\n        tokens[0] = pool.coins(0);\n        tokens[1] = pool.coins(1);\n        uint256 value = msg.value;\n        for (uint256 i; i < tokens.length; i = _inc(i)) {\n            if (amounts[i] == 0) continue;\n            if (tokens[i] == address(weth) && value > 0) {\n                if (amounts[i] != value) revert AmountMismatch();\n                weth.deposit{value: value}();\n            } else {\n                ERC20(tokens[i]).safeTransferFrom(msg.sender, address(this), amounts[i]);\n            }\n            IERC20(tokens[i]).approve(address(pool), amounts[i]);\n        }\n\n        lptokenAmount = pool.add_liquidity(amounts, lptokenAmount, false, address(this));\n\n        // Stake CPT to receive cvxtoken\n        IERC20(lptoken).approve(address(booster), lptokenAmount);\n        booster.deposit(pid, lptokenAmount, false);\n\n        // stake cvxtoken\n        shares = _deposit(receiver);\n\n        // refund dust\n        for (uint256 i; i < tokens.length; i = _inc(i)) {\n            address token = tokens[i];\n            uint256 amount = IERC20(token).balanceOf(address(this));\n            // if token is weth, check refund is more than value transfer fee\n            if (token == address(weth) && 50000 * block.basefee > amount) {\n                continue;\n            }\n            ERC20(token).safeTransfer(msg.sender, amount);\n        }\n    }\n\n    /**\n     * @dev Zap out of the Curve pool by redeeming staked LP tokens and receiving underlying tokens in return.\n     * @param shares Number of shares representing the staked LP tokens to be redeemed.\n     * @param receiver Address to receive the redeemed underlying tokens.\n     * @param owner Address of the owner initiating the withdrawal.\n     * @param minAmountsOut Minimum amounts of underlying tokens to be received in the redemption.\n     * @return amountsOut Array of amounts representing the redeemed underlying tokens.\n     */\n    function zipCPT(\n        uint256 shares,\n        address receiver,\n        address owner,\n        uint256[2] calldata minAmountsOut\n    ) public virtual returns (uint256[] memory amountsOut) {\n        if (shares > maxRedeem(owner)) revert WithdrawMoreThanMax();\n\n        uint256 assets = previewRedeem(shares);\n\n        if (msg.sender != owner) {\n            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.\n            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;\n        }\n\n        if (lastDepositTimestamp[owner] + minLockDuration > block.timestamp) revert TimeLocked();\n\n        // Receive CPT\n        crvRewards.withdraw(assets, false);\n        IERC20(cvxtoken).approve(address(booster), assets);\n        booster.withdraw(pid, assets);\n\n        _burn(owner, shares);\n\n        // Exit CPT\n        address[] memory tokens = new address[](2);\n        tokens[0] = pool.coins(0);\n        tokens[1] = pool.coins(1);\n        if (minAmountsOut[0] == 0) {\n            pool.remove_liquidity_one_coin(assets, 1, minAmountsOut[1]);\n        } else if (minAmountsOut[1] == 0) {\n            pool.remove_liquidity_one_coin(assets, 0, minAmountsOut[0]);\n        } else {\n            pool.remove_liquidity(assets, minAmountsOut);\n        }\n        amountsOut = new uint256[](tokens.length);\n        for (uint256 i; i < tokens.length; i = _inc(i)) {\n            amountsOut[i] = IERC20(tokens[i]).balanceOf(address(this));\n            ERC20(tokens[i]).safeTransfer(receiver, amountsOut[i]);\n        }\n\n        emit Withdraw(msg.sender, receiver, owner, assets, shares);\n    }\n\n    function _getStashToken(address virtualRewards) internal override returns (address stashToken) {\n        address stash = IVirtualRewards(virtualRewards).rewardToken();\n        stashToken = ITokenWrapper(stash).token();\n    }\n\n    /// @dev swap reward token for weth for compound staking\n    function swapReward(address tokenIn, uint256 amountIn) internal virtual override returns (uint256 amountOut) {\n        if (tokenIn == CRV) {\n            // CRV\n            ERC20(tokenIn).approve(address(crvPool), amountIn);\n            amountOut = crvPool.exchange(2, 1, amountIn, 1, false, address(this));\n        } else {\n            // assume CVX\n            ERC20(tokenIn).approve(address(cvxPool), amountIn);\n            amountOut = cvxPool.exchange(1, 0, amountIn, 1, false);\n            // weth.deposit{value: amountOut}();\n        }\n    }\n\n    /// @dev zap weth rewards into LP, then stake\n    /// note: assumes weth is one of the tokens\n    function _zapSwappedRewards(uint256 amount) internal virtual override {\n        {\n            uint256 wethBal = weth.balanceOf(address(this));\n            if (amount > wethBal) return;\n            if (wethBal < MIN_ZAP) return;\n            if (amount < wethBal) amount = wethBal;\n        }\n\n        // step 1: swap weth for mevEth fully\n        uint256 shares = mevEth.deposit(amount, address(this));\n        uint256[2] memory amounts;\n        {\n            address[2] memory tokens;\n            tokens[0] = pool.coins(0);\n            tokens[1] = pool.coins(1);\n            for (uint256 i; i < tokens.length; i = _inc(i)) {\n                if (tokens[i] == address(mevEth)) {\n                    amounts[i] = shares;\n                }\n            }\n        }\n\n        // step 2: zap tokens for LP\n        {\n            uint256 lptokenAmount = ((pool.calc_token_amount(amounts) - 1) * 99) / 100;\n            lptokenAmount = pool.add_liquidity(amounts, lptokenAmount, false, address(this));\n            // step 3: Stake CPT to receive cvxtoken\n            IERC20(lptoken).approve(address(booster), lptokenAmount);\n            booster.deposit(pid, lptokenAmount, false);\n        }\n\n        // step 4: stake cvxtoken\n        {\n            uint256 assets = IERC20(cvxtoken).balanceOf(address(this));\n            emit CompoundRewards(assets);\n            afterDeposit(assets, 0);\n        }\n    }\n}\n"
      },
      "contracts/StakedPT.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.18;\n\n// Import necessary contracts and libraries\nimport \"./Owned.sol\";\nimport \"solmate/mixins/ERC4626.sol\";\nimport \"solmate/utils/ReentrancyGuard.sol\";\nimport \"solmate/utils/SafeTransferLib.sol\";\nimport \"./interfaces/IBooster.sol\";\nimport \"./interfaces/IRewards.sol\";\nimport \"./interfaces/IERC20.sol\";\nimport \"./interfaces/IVirtualRewards.sol\";\nimport \"./interfaces/IStash.sol\";\nimport \"./interfaces/IMevEth.sol\";\nimport \"./interfaces/IWETH.sol\";\n\n/**\n * @title StakedPT\n * @dev StakedPT is a contract that represents staked PT / LP (Pool Token / Liquidity Provider) tokens,\n * allowing users to stake their LP tokens to earn rewards in another token (cvxtoken).\n * This contract extends ERC4626, implements ReentrancyGuard, and is Owned.\n * Ref: https://docs.convexfinance.com/convexfinanceintegration/booster\n */\nabstract contract StakedPT is ERC4626, ReentrancyGuard, Owned {\n    using SafeTransferLib for ERC20;\n\n    // Immutables\n    /// @notice underlying token(ex. the curve lp token)\n    address public immutable lptoken;\n    /// @notice convex deposit token(a 1:1 token representing an lp deposit)\n    address public immutable cvxtoken;\n    /// @notice main deposit contract for LP tokens\n    IBooster public immutable booster;\n    /// @notice main reward contract for the pool\n    IRewards public immutable crvRewards;\n    /// @notice Wrapped Ether (WETH) contract\n    IWETH public immutable weth;\n    /// @notice Pool ID in the rewards pool contract\n    uint256 public immutable pid;\n\n    IMevEth internal constant mevEth = IMevEth(0x24Ae2dA0f361AA4BE46b48EB19C91e02c5e4f27E);\n    uint256 internal constant MIN_ZAP = 0.01 ether;\n\n    // Globals\n    address public treasury;\n    uint256 public minLockDuration;\n    mapping(address => uint256) public lastDepositTimestamp;\n\n    // Events\n    event UpdateTreasury(address indexed treasury);\n    event UpdateMinLockDuration(uint256 duration);\n    event CompoundRewards(uint256 assets);\n\n    // Custom errors\n    error ZeroShares();\n    error TimeLocked();\n    error AmountMismatch();\n    error WithdrawMoreThanMax();\n\n    /**\n     * @dev Constructor to initialize the StakedBPT contract.\n     * @param _lptoken Address of the LP token\n     * @param _cvxtoken convex deposit token(a 1:1 token representing an lp deposit)\n     * @param _booster Address of the booster contract\n     * @param _treasury Address of the treasury\n     * @param _owner Address of the contract owner\n     * @param _minLockDuration Minimum lock duration for staked LP tokens\n     * @param _weth Address of the Wrapped Ether (WETH) contract\n     * @param _pid Pool ID in the rewards pool contract\n     */\n    constructor(\n        address _lptoken,\n        address _cvxtoken,\n        address _booster,\n        address _treasury,\n        address _owner,\n        uint256 _minLockDuration,\n        address _weth,\n        uint256 _pid\n    )\n        ERC4626(\n            ERC20(_cvxtoken),\n            string(abi.encodePacked(\"Staked \", IERC20(_lptoken).name())),\n            string(abi.encodePacked(\"stk\", IERC20(_lptoken).symbol()))\n        )\n        Owned(_owner)\n    {\n        booster = IBooster(_booster);\n\n        treasury = _treasury;\n        minLockDuration = _minLockDuration;\n        pid = _pid;\n        weth = IWETH(_weth);\n\n        IBooster.PoolInfo memory info = booster.poolInfo(_pid);\n        lptoken = _lptoken;\n        cvxtoken = _cvxtoken;\n        crvRewards = IRewards(info.crvRewards);\n\n        // Emit initialization events\n        emit UpdateTreasury(_treasury);\n        emit UpdateMinLockDuration(_minLockDuration);\n    }\n\n    /**\n     * @dev View function to get the total assets held by the contract.\n     * @return uint256 representing the total assets held by the contract.\n     */\n    function totalAssets() public view virtual override returns (uint256) {\n        return crvRewards.balanceOf(address(this));\n    }\n\n    /**\n     * @dev Update the treasury address. Only callable by the owner.\n     * @param _treasury New treasury address.\n     */\n    function updateTreasury(address _treasury) external onlyOwner {\n        treasury = _treasury;\n\n        emit UpdateTreasury(_treasury);\n    }\n\n    /**\n     * @dev Update the minimum lock duration for staked LP tokens. Only callable by the owner.\n     * @param _minLockDuration New minimum lock duration.\n     */\n    function updateMinLockDuration(uint256 _minLockDuration) external onlyOwner {\n        minLockDuration = _minLockDuration;\n\n        emit UpdateMinLockDuration(_minLockDuration);\n    }\n\n    function _deposit(address receiver) internal returns (uint256 shares) {\n        uint256 assets = IERC20(cvxtoken).balanceOf(address(this));\n\n        // Check for rounding error since we round down in previewDeposit.\n        shares = previewDeposit(assets);\n        if (shares == 0) revert ZeroShares();\n\n        _updateDepositTimestamp(receiver, shares);\n\n        _mint(receiver, shares);\n\n        emit Deposit(msg.sender, receiver, assets, shares);\n\n        afterDeposit(assets, shares);\n    }\n\n    /**\n     * @dev Deposit LP tokens to stake and receive AUR / CVX rewards.\n     * @param lptokenAmount Amount of LP tokens to deposit.\n     * @param receiver Address to receive the staked LP tokens.\n     * @return shares Number of shares representing the staked LP tokens.\n     */\n    function depositLP(uint256 lptokenAmount, address receiver) public virtual returns (uint256 shares) {\n        ERC20(lptoken).safeTransferFrom(msg.sender, address(this), lptokenAmount);\n\n        // Stake BPT to receive cvxtoken\n        IERC20(lptoken).approve(address(booster), lptokenAmount);\n        booster.deposit(pid, lptokenAmount, false);\n\n        // stake cvxtoken\n        shares = _deposit(receiver);\n    }\n\n    function withdrawLP(\n        uint256 shares,\n        address receiver,\n        address owner\n    ) public virtual returns (uint256 lptokenAmount) {\n        if (shares > maxRedeem(owner)) revert WithdrawMoreThanMax();\n\n        uint256 assets = previewRedeem(shares);\n\n        if (msg.sender != owner) {\n            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.\n            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;\n        }\n\n        if (lastDepositTimestamp[owner] + minLockDuration > block.timestamp) revert TimeLocked();\n\n        // Receive LP\n        crvRewards.withdraw(assets, false);\n        IERC20(cvxtoken).approve(address(booster), assets);\n        booster.withdraw(pid, assets);\n\n        _burn(owner, shares);\n\n        // Transfer LP\n        lptokenAmount = IERC20(lptoken).balanceOf(address(this));\n        ERC20(lptoken).safeTransfer(receiver, lptokenAmount);\n\n        emit Withdraw(msg.sender, receiver, owner, assets, shares);\n    }\n\n    /**\n     * @dev Internal function executed after a successful deposit.\n     * @param assets Amount of cvxtoken received after staking BPT.\n     */\n    function afterDeposit(uint256 assets, uint256) internal override {\n        IERC20(cvxtoken).approve(address(crvRewards), assets);\n        crvRewards.stake(assets);\n    }\n\n    /**\n     * @dev Internal function executed before a withdrawal to check withdrawal conditions.\n     * @param assets Amount of cvxtoken to be withdrawn.\n     */\n    function beforeWithdraw(uint256 assets, uint256) internal override {\n        // Receive cvxtoken\n        crvRewards.withdraw(assets, false);\n    }\n\n    function harvest() external {\n        booster.earmarkRewards(pid);\n        crvRewards.getReward();\n        address token = crvRewards.rewardToken();\n        uint256 amount = IERC20(token).balanceOf(address(this));\n        uint256 amountOut;\n        if (amount > 0) {\n            // swap for weth\n            amountOut = swapReward(token, amount);\n        }\n        amountOut += _claimExtras();\n        amountOut = _sendProtocolFee(amountOut);\n        _zapSwappedRewards(amountOut);\n    }\n\n    function _claimExtras() internal virtual returns (uint256 amountOut) {\n        uint256 len = crvRewards.extraRewardsLength();\n        if (len > 0) {\n            for (uint256 i; i < len; i = _inc(i)) {\n                address virtualRewards = crvRewards.extraRewards(i);\n                address token = _getStashToken(virtualRewards);\n                uint256 amount = IERC20(token).balanceOf(address(this));\n                if (amount > 0) {\n                    amountOut += swapReward(token, amount);\n                }\n            }\n        }\n    }\n\n    function swapReward(address token, uint256 amountIn) internal virtual returns (uint256 amountOut) {}\n\n    function _zapSwappedRewards(uint256 amount) internal virtual {}\n\n    function _sendProtocolFee(uint256 wethBal) internal returns (uint256 amountRemaining) {\n        // send 10% to treasury\n        ERC20(address(weth)).safeTransfer(treasury, wethBal / 10);\n        amountRemaining = (wethBal * 90) / 100;\n    }\n\n    function _getStashToken(address virtualRewards) internal virtual returns (address stashToken) {\n        address stash = IVirtualRewards(virtualRewards).rewardToken();\n        stashToken = IStash(stash).baseToken();\n    }\n\n    function _inc(uint256 i) internal pure returns (uint256 j) {\n        unchecked {\n            j = i + 1;\n        }\n    }\n\n    // override ERC4626 functions to update deposit timestamp\n\n    function deposit(uint256 assets, address receiver) public virtual override returns (uint256 shares) {\n        // Set the deposit timestamp for the user\n        _updateDepositTimestamp(receiver, previewDeposit(assets));\n        shares = super.deposit(assets, receiver);\n    }\n\n    function mint(uint256 shares, address receiver) public virtual override returns (uint256 assets) {\n        // Set the deposit timestamp for the user\n        _updateDepositTimestamp(receiver, shares);\n        assets = super.mint(shares, receiver);\n    }\n\n    function withdraw(\n        uint256 assets,\n        address receiver,\n        address owner\n    ) public virtual override returns (uint256 shares) {\n        if (lastDepositTimestamp[owner] + minLockDuration > block.timestamp) revert TimeLocked();\n        shares = super.withdraw(assets, receiver, owner);\n    }\n\n    function redeem(uint256 shares, address receiver, address owner) public virtual override returns (uint256 assets) {\n        if (lastDepositTimestamp[owner] + minLockDuration > block.timestamp) revert TimeLocked();\n        assets = super.redeem(shares, receiver, owner);\n    }\n\n    function _updateDepositTimestamp(address account, uint256 shares) internal {\n        // Set the deposit timestamp for the user\n        uint256 prevBalance = balanceOf[account];\n        uint256 lastDeposit = lastDepositTimestamp[account];\n        if (prevBalance == 0 || lastDeposit == 0) {\n            lastDepositTimestamp[account] = block.timestamp;\n        } else {\n            // multiple deposits, so weight timestamp by amounts\n            unchecked {\n                lastDepositTimestamp[account] =\n                    lastDeposit +\n                    ((block.timestamp - lastDeposit) * shares) /\n                    (prevBalance + shares);\n            }\n        }\n    }\n\n    function _updateTransferTimestamp(address account, uint256 shares) internal {\n        // Set the transfer timestamp for the user\n        uint256 newBalance = balanceOf[account];\n        uint256 lastDeposit = lastDepositTimestamp[account];\n        if (newBalance == 0 || lastDeposit < (block.timestamp - lastDeposit)) {\n            lastDepositTimestamp[account] = 0;\n        } else {\n            // multiple deposits, so weight timestamp by amounts\n            unchecked {\n                lastDepositTimestamp[account] =\n                    lastDeposit -\n                    ((block.timestamp - lastDeposit) * shares) /\n                    (newBalance + shares);\n            }\n        }\n    }\n\n    function transfer(address to, uint256 amount) public virtual override returns (bool success) {\n        _updateDepositTimestamp(to, amount);\n        success = super.transfer(to, amount);\n        _updateTransferTimestamp(msg.sender, amount);\n    }\n\n    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool success) {\n        _updateDepositTimestamp(to, amount);\n        success = super.transferFrom(from, to, amount);\n        _updateTransferTimestamp(from, amount);\n    }\n}\n"
      },
      "contracts/interfaces/ITokenWrapper.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ninterface ITokenWrapper {\n    function token() external view returns (address);\n}\n"
      },
      "contracts/interfaces/ICurveV2Pool.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ninterface ICurveV2Pool {\n    function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount) external payable returns (uint256);\n\n    function add_liquidity(\n        uint256[2] calldata amounts,\n        uint256 min_mint_amount,\n        bool use_eth\n    ) external payable returns (uint256);\n\n    function add_liquidity(\n        uint256[2] calldata amounts,\n        uint256 min_mint_amount,\n        bool use_eth,\n        address receiver\n    ) external payable returns (uint256);\n\n    function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts) external;\n\n    function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts, bool use_eth) external;\n\n    function remove_liquidity(\n        uint256 _amount,\n        uint256[2] calldata min_amounts,\n        bool use_eth,\n        address receiver\n    ) external;\n\n    function remove_liquidity_one_coin(uint256 token_amount, uint256 i, uint256 min_amount) external returns (uint256);\n\n    function remove_liquidity_one_coin(\n        uint256 token_amount,\n        uint256 i,\n        uint256 min_amount,\n        bool use_eth\n    ) external returns (uint256);\n\n    function remove_liquidity_one_coin(\n        uint256 token_amount,\n        uint256 i,\n        uint256 min_amount,\n        bool use_eth,\n        address receiver\n    ) external returns (uint256);\n\n    function calc_token_amount(uint256[2] calldata amounts) external view returns (uint256);\n\n    function calc_withdraw_one_coin(uint256 token_amount, uint256 i) external view returns (uint256);\n\n    function lp_price() external view returns (uint256);\n\n    function token() external view returns (address);\n\n    function coins(uint256 arg0) external view returns (address);\n\n    function balances(uint256 arg0) external view returns (uint256);\n\n    function exchange(\n        uint256 i,\n        uint256 j,\n        uint256 dx,\n        uint256 min_dy,\n        bool use_eth,\n        address receiver\n    ) external payable returns (uint256 dy);\n\n    function exchange(\n        uint256 i,\n        uint256 j,\n        uint256 dx,\n        uint256 min_dy,\n        bool use_eth\n    ) external payable returns (uint256 dy);\n\n    function get_dy(uint256 i, uint256 j, uint256 dx) external view returns (uint256);\n}\n"
      },
      "contracts/Owned.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Owned.sol)\nabstract contract Owned {\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event OwnerUpdated(address indexed user, address indexed newOwner);\n\n    /*//////////////////////////////////////////////////////////////\n                            OWNERSHIP STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    address public OWNER;\n\n    modifier onlyOwner() virtual {\n        require(msg.sender == OWNER, \"UNAUTHORIZED\");\n\n        _;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                               CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n    constructor(address _owner) {\n        OWNER = _owner;\n\n        emit OwnerUpdated(address(0), _owner);\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                             OWNERSHIP LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function setOwner(address newOwner) public virtual onlyOwner {\n        OWNER = newOwner;\n\n        emit OwnerUpdated(msg.sender, newOwner);\n    }\n}\n"
      },
      "lib/solmate/src/mixins/ERC4626.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"../tokens/ERC20.sol\";\nimport {SafeTransferLib} from \"../utils/SafeTransferLib.sol\";\nimport {FixedPointMathLib} from \"../utils/FixedPointMathLib.sol\";\n\n/// @notice Minimal ERC4626 tokenized Vault implementation.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol)\nabstract contract ERC4626 is ERC20 {\n    using SafeTransferLib for ERC20;\n    using FixedPointMathLib for uint256;\n\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n    event Withdraw(\n        address indexed caller,\n        address indexed receiver,\n        address indexed owner,\n        uint256 assets,\n        uint256 shares\n    );\n\n    /*//////////////////////////////////////////////////////////////\n                               IMMUTABLES\n    //////////////////////////////////////////////////////////////*/\n\n    ERC20 public immutable asset;\n\n    constructor(\n        ERC20 _asset,\n        string memory _name,\n        string memory _symbol\n    ) ERC20(_name, _symbol, _asset.decimals()) {\n        asset = _asset;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        DEPOSIT/WITHDRAWAL LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {\n        // Check for rounding error since we round down in previewDeposit.\n        require((shares = previewDeposit(assets)) != 0, \"ZERO_SHARES\");\n\n        // Need to transfer before minting or ERC777s could reenter.\n        asset.safeTransferFrom(msg.sender, address(this), assets);\n\n        _mint(receiver, shares);\n\n        emit Deposit(msg.sender, receiver, assets, shares);\n\n        afterDeposit(assets, shares);\n    }\n\n    function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {\n        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.\n\n        // Need to transfer before minting or ERC777s could reenter.\n        asset.safeTransferFrom(msg.sender, address(this), assets);\n\n        _mint(receiver, shares);\n\n        emit Deposit(msg.sender, receiver, assets, shares);\n\n        afterDeposit(assets, shares);\n    }\n\n    function withdraw(\n        uint256 assets,\n        address receiver,\n        address owner\n    ) public virtual returns (uint256 shares) {\n        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.\n\n        if (msg.sender != owner) {\n            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.\n\n            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;\n        }\n\n        beforeWithdraw(assets, shares);\n\n        _burn(owner, shares);\n\n        emit Withdraw(msg.sender, receiver, owner, assets, shares);\n\n        asset.safeTransfer(receiver, assets);\n    }\n\n    function redeem(\n        uint256 shares,\n        address receiver,\n        address owner\n    ) public virtual returns (uint256 assets) {\n        if (msg.sender != owner) {\n            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.\n\n            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;\n        }\n\n        // Check for rounding error since we round down in previewRedeem.\n        require((assets = previewRedeem(shares)) != 0, \"ZERO_ASSETS\");\n\n        beforeWithdraw(assets, shares);\n\n        _burn(owner, shares);\n\n        emit Withdraw(msg.sender, receiver, owner, assets, shares);\n\n        asset.safeTransfer(receiver, assets);\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                            ACCOUNTING LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function totalAssets() public view virtual returns (uint256);\n\n    function convertToShares(uint256 assets) public view virtual returns (uint256) {\n        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.\n\n        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());\n    }\n\n    function convertToAssets(uint256 shares) public view virtual returns (uint256) {\n        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.\n\n        return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);\n    }\n\n    function previewDeposit(uint256 assets) public view virtual returns (uint256) {\n        return convertToShares(assets);\n    }\n\n    function previewMint(uint256 shares) public view virtual returns (uint256) {\n        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.\n\n        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);\n    }\n\n    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {\n        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.\n\n        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());\n    }\n\n    function previewRedeem(uint256 shares) public view virtual returns (uint256) {\n        return convertToAssets(shares);\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                     DEPOSIT/WITHDRAWAL LIMIT LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function maxDeposit(address) public view virtual returns (uint256) {\n        return type(uint256).max;\n    }\n\n    function maxMint(address) public view virtual returns (uint256) {\n        return type(uint256).max;\n    }\n\n    function maxWithdraw(address owner) public view virtual returns (uint256) {\n        return convertToAssets(balanceOf[owner]);\n    }\n\n    function maxRedeem(address owner) public view virtual returns (uint256) {\n        return balanceOf[owner];\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                          INTERNAL HOOKS LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}\n\n    function afterDeposit(uint256 assets, uint256 shares) internal virtual {}\n}\n"
      },
      "lib/solmate/src/utils/ReentrancyGuard.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/// @notice Gas optimized reentrancy protection for smart contracts.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol)\n/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)\nabstract contract ReentrancyGuard {\n    uint256 private locked = 1;\n\n    modifier nonReentrant() virtual {\n        require(locked == 1, \"REENTRANCY\");\n\n        locked = 2;\n\n        _;\n\n        locked = 1;\n    }\n}\n"
      },
      "lib/solmate/src/utils/SafeTransferLib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"../tokens/ERC20.sol\";\n\n/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)\n/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.\nlibrary SafeTransferLib {\n    /*//////////////////////////////////////////////////////////////\n                             ETH OPERATIONS\n    //////////////////////////////////////////////////////////////*/\n\n    function safeTransferETH(address to, uint256 amount) internal {\n        bool success;\n\n        assembly {\n            // Transfer the ETH and store if it succeeded or not.\n            success := call(gas(), to, amount, 0, 0, 0, 0)\n        }\n\n        require(success, \"ETH_TRANSFER_FAILED\");\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                            ERC20 OPERATIONS\n    //////////////////////////////////////////////////////////////*/\n\n    function safeTransferFrom(\n        ERC20 token,\n        address from,\n        address to,\n        uint256 amount\n    ) internal {\n        bool success;\n\n        assembly {\n            // We'll write our calldata to this slot below, but restore it later.\n            let memPointer := mload(0x40)\n\n            // Write the abi-encoded calldata into memory, beginning with the function selector.\n            mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n            mstore(4, from) // Append the \"from\" argument.\n            mstore(36, to) // Append the \"to\" argument.\n            mstore(68, amount) // Append the \"amount\" argument.\n\n            success := and(\n                // Set success to whether the call reverted, if not we check it either\n                // returned exactly 1 (can't just be non-zero data), or had no return data.\n                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n                // We use 100 because that's the total length of our calldata (4 + 32 * 3)\n                // Counterintuitively, this call() must be positioned after the or() in the\n                // surrounding and() because and() evaluates its arguments from right to left.\n                call(gas(), token, 0, 0, 100, 0, 32)\n            )\n\n            mstore(0x60, 0) // Restore the zero slot to zero.\n            mstore(0x40, memPointer) // Restore the memPointer.\n        }\n\n        require(success, \"TRANSFER_FROM_FAILED\");\n    }\n\n    function safeTransfer(\n        ERC20 token,\n        address to,\n        uint256 amount\n    ) internal {\n        bool success;\n\n        assembly {\n            // We'll write our calldata to this slot below, but restore it later.\n            let memPointer := mload(0x40)\n\n            // Write the abi-encoded calldata into memory, beginning with the function selector.\n            mstore(0, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n            mstore(4, to) // Append the \"to\" argument.\n            mstore(36, amount) // Append the \"amount\" argument.\n\n            success := and(\n                // Set success to whether the call reverted, if not we check it either\n                // returned exactly 1 (can't just be non-zero data), or had no return data.\n                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n                // We use 68 because that's the total length of our calldata (4 + 32 * 2)\n                // Counterintuitively, this call() must be positioned after the or() in the\n                // surrounding and() because and() evaluates its arguments from right to left.\n                call(gas(), token, 0, 0, 68, 0, 32)\n            )\n\n            mstore(0x60, 0) // Restore the zero slot to zero.\n            mstore(0x40, memPointer) // Restore the memPointer.\n        }\n\n        require(success, \"TRANSFER_FAILED\");\n    }\n\n    function safeApprove(\n        ERC20 token,\n        address to,\n        uint256 amount\n    ) internal {\n        bool success;\n\n        assembly {\n            // We'll write our calldata to this slot below, but restore it later.\n            let memPointer := mload(0x40)\n\n            // Write the abi-encoded calldata into memory, beginning with the function selector.\n            mstore(0, 0x095ea7b300000000000000000000000000000000000000000000000000000000)\n            mstore(4, to) // Append the \"to\" argument.\n            mstore(36, amount) // Append the \"amount\" argument.\n\n            success := and(\n                // Set success to whether the call reverted, if not we check it either\n                // returned exactly 1 (can't just be non-zero data), or had no return data.\n                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n                // We use 68 because that's the total length of our calldata (4 + 32 * 2)\n                // Counterintuitively, this call() must be positioned after the or() in the\n                // surrounding and() because and() evaluates its arguments from right to left.\n                call(gas(), token, 0, 0, 68, 0, 32)\n            )\n\n            mstore(0x60, 0) // Restore the zero slot to zero.\n            mstore(0x40, memPointer) // Restore the memPointer.\n        }\n\n        require(success, \"APPROVE_FAILED\");\n    }\n}\n"
      },
      "contracts/interfaces/IBooster.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ninterface IBooster {\n    /// @notice Pool Info for deposits and staking\n    /// @custom: lptoken:  the underlying token(ex. the curve lp token)\n    /// @custom: cvxtoken: the convex deposit token(a 1:1 token representing an lp deposit).  The supply of this token can be used to calculate the TVL of the pool\n    /// @custom: gauge: the curve \"gauge\" or staking contract used by the pool\n    /// @custom: crvRewards: the main reward contract for the pool\n    /// @custom: stash: a helper contract used to hold extra rewards (like snx) on behalf of the pool until distribution is called\n    /// @custom: shutdown: a shutdown flag of the pool\n    struct PoolInfo {\n        address lptoken;\n        address cvxtoken;\n        address gauge;\n        address crvRewards;\n        address stash;\n        bool shutdown;\n    }\n\n    function poolInfo(uint256 _pid) external returns (PoolInfo memory);\n\n    //deposit into convex, receive a tokenized deposit.  parameter to stake immediately\n    function deposit(uint256 _pid, uint256 _amount, bool _stake) external returns (bool);\n\n    //burn a tokenized deposit to receive curve lp tokens back\n    function withdraw(uint256 _pid, uint256 _amount) external returns (bool);\n\n    function earmarkRewards(uint256 pid) external;\n}\n"
      },
      "contracts/interfaces/IRewards.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ninterface IRewards {\n    //get balance of an address\n    function balanceOf(address _account) external view returns (uint256);\n\n    //withdraw to a convex tokenized deposit\n    function withdraw(uint256 _amount, bool _claim) external returns (bool);\n\n    //withdraw directly to curve LP token\n    function withdrawAndUnwrap(uint256 _amount, bool _claim) external returns (bool);\n\n    //claim rewards\n    function getReward() external returns (bool);\n\n    //stake a convex tokenized deposit\n    function stake(uint256 _amount) external returns (bool);\n\n    //stake a convex tokenized deposit for another address(transfering ownership)\n    function stakeFor(address _account, uint256 _amount) external returns (bool);\n\n    function exit() external returns (bool);\n\n    function rewardToken() external view returns (address);\n\n    function extraRewards(uint256) external view returns (address);\n\n    function extraRewardsLength() external view returns (uint256);\n}\n"
      },
      "contracts/interfaces/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"
      },
      "contracts/interfaces/IVirtualRewards.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ninterface IVirtualRewards {\n    function rewardToken() external returns (address);\n}\n"
      },
      "contracts/interfaces/IStash.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ninterface IStash {\n    function baseToken() external returns (address);\n}\n"
      },
      "contracts/interfaces/IMevEth.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport \"./IERC20.sol\";\n\ninterface IMevEth is IERC20 {\n    function fraction() external view returns (uint128 elastic, uint128 base);\n\n    function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n    function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n    function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n    function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n    function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n    function previewMint(uint256 shares) external view returns (uint256 assets);\n\n    function deposit(uint256 assets, address receiver) external payable returns (uint256 shares);\n\n    function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);\n\n    function withdrawQueue(uint256 assets, address receiver, address owner) external returns (uint256 shares);\n}\n"
      },
      "contracts/interfaces/IWETH.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport \"./IERC20.sol\";\n\ninterface IWETH is IERC20 {\n    function deposit() external payable;\n\n    function withdraw(uint) external;\n}\n"
      },
      "lib/solmate/src/tokens/ERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)\n/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)\n/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.\nabstract contract ERC20 {\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event Transfer(address indexed from, address indexed to, uint256 amount);\n\n    event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n    /*//////////////////////////////////////////////////////////////\n                            METADATA STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    string public name;\n\n    string public symbol;\n\n    uint8 public immutable decimals;\n\n    /*//////////////////////////////////////////////////////////////\n                              ERC20 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 public totalSupply;\n\n    mapping(address => uint256) public balanceOf;\n\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    /*//////////////////////////////////////////////////////////////\n                            EIP-2612 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal immutable INITIAL_CHAIN_ID;\n\n    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;\n\n    mapping(address => uint256) public nonces;\n\n    /*//////////////////////////////////////////////////////////////\n                               CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n    constructor(\n        string memory _name,\n        string memory _symbol,\n        uint8 _decimals\n    ) {\n        name = _name;\n        symbol = _symbol;\n        decimals = _decimals;\n\n        INITIAL_CHAIN_ID = block.chainid;\n        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                               ERC20 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function approve(address spender, uint256 amount) public virtual returns (bool) {\n        allowance[msg.sender][spender] = amount;\n\n        emit Approval(msg.sender, spender, amount);\n\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) public virtual returns (bool) {\n        balanceOf[msg.sender] -= amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(msg.sender, to, amount);\n\n        return true;\n    }\n\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) public virtual returns (bool) {\n        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.\n\n        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;\n\n        balanceOf[from] -= amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(from, to, amount);\n\n        return true;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                             EIP-2612 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) public virtual {\n        require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n        // Unchecked because the only math done is incrementing\n        // the owner's nonce which cannot realistically overflow.\n        unchecked {\n            address recoveredAddress = ecrecover(\n                keccak256(\n                    abi.encodePacked(\n                        \"\\x19\\x01\",\n                        DOMAIN_SEPARATOR(),\n                        keccak256(\n                            abi.encode(\n                                keccak256(\n                                    \"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n                                ),\n                                owner,\n                                spender,\n                                value,\n                                nonces[owner]++,\n                                deadline\n                            )\n                        )\n                    )\n                ),\n                v,\n                r,\n                s\n            );\n\n            require(recoveredAddress != address(0) && recoveredAddress == owner, \"INVALID_SIGNER\");\n\n            allowance[recoveredAddress][spender] = value;\n        }\n\n        emit Approval(owner, spender, value);\n    }\n\n    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\n        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();\n    }\n\n    function computeDomainSeparator() internal view virtual returns (bytes32) {\n        return\n            keccak256(\n                abi.encode(\n                    keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n                    keccak256(bytes(name)),\n                    keccak256(\"1\"),\n                    block.chainid,\n                    address(this)\n                )\n            );\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        INTERNAL MINT/BURN LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function _mint(address to, uint256 amount) internal virtual {\n        totalSupply += amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(address(0), to, amount);\n    }\n\n    function _burn(address from, uint256 amount) internal virtual {\n        balanceOf[from] -= amount;\n\n        // Cannot underflow because a user's balance\n        // will never be larger than the total supply.\n        unchecked {\n            totalSupply -= amount;\n        }\n\n        emit Transfer(from, address(0), amount);\n    }\n}\n"
      },
      "lib/solmate/src/utils/FixedPointMathLib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/// @notice Arithmetic library with operations for fixed-point numbers.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol)\nlibrary FixedPointMathLib {\n    /*//////////////////////////////////////////////////////////////\n                    SIMPLIFIED FIXED POINT OPERATIONS\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.\n\n    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {\n        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.\n    }\n\n    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {\n        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.\n    }\n\n    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {\n        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.\n    }\n\n    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {\n        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.\n    }\n\n    function powWad(int256 x, int256 y) internal pure returns (int256) {\n        // Equivalent to x to the power of y because x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)\n        return expWad((lnWad(x) * y) / int256(WAD)); // Using ln(x) means x must be greater than 0.\n    }\n\n    function expWad(int256 x) internal pure returns (int256 r) {\n        unchecked {\n            // When the result is < 0.5 we return zero. This happens when\n            // x <= floor(log(0.5e18) * 1e18) ~ -42e18\n            if (x <= -42139678854452767551) return 0;\n\n            // When the result is > (2**255 - 1) / 1e18 we can not represent it as an\n            // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135.\n            if (x >= 135305999368893231589) revert(\"EXP_OVERFLOW\");\n\n            // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96\n            // for more intermediate precision and a binary basis. This base conversion\n            // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.\n            x = (x << 78) / 5**18;\n\n            // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers\n            // of two such that exp(x) = exp(x') * 2**k, where k is an integer.\n            // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).\n            int256 k = ((x << 96) / 54916777467707473351141471128 + 2**95) >> 96;\n            x = x - k * 54916777467707473351141471128;\n\n            // k is in the range [-61, 195].\n\n            // Evaluate using a (6, 7)-term rational approximation.\n            // p is made monic, we'll multiply by a scale factor later.\n            int256 y = x + 1346386616545796478920950773328;\n            y = ((y * x) >> 96) + 57155421227552351082224309758442;\n            int256 p = y + x - 94201549194550492254356042504812;\n            p = ((p * y) >> 96) + 28719021644029726153956944680412240;\n            p = p * x + (4385272521454847904659076985693276 << 96);\n\n            // We leave p in 2**192 basis so we don't need to scale it back up for the division.\n            int256 q = x - 2855989394907223263936484059900;\n            q = ((q * x) >> 96) + 50020603652535783019961831881945;\n            q = ((q * x) >> 96) - 533845033583426703283633433725380;\n            q = ((q * x) >> 96) + 3604857256930695427073651918091429;\n            q = ((q * x) >> 96) - 14423608567350463180887372962807573;\n            q = ((q * x) >> 96) + 26449188498355588339934803723976023;\n\n            assembly {\n                // Div in assembly because solidity adds a zero check despite the unchecked.\n                // The q polynomial won't have zeros in the domain as all its roots are complex.\n                // No scaling is necessary because p is already 2**96 too large.\n                r := sdiv(p, q)\n            }\n\n            // r should be in the range (0.09, 0.25) * 2**96.\n\n            // We now need to multiply r by:\n            // * the scale factor s = ~6.031367120.\n            // * the 2**k factor from the range reduction.\n            // * the 1e18 / 2**96 factor for base conversion.\n            // We do this all at once, with an intermediate result in 2**213\n            // basis, so the final right shift is always by a positive amount.\n            r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k));\n        }\n    }\n\n    function lnWad(int256 x) internal pure returns (int256 r) {\n        unchecked {\n            require(x > 0, \"UNDEFINED\");\n\n            // We want to convert x from 10**18 fixed point to 2**96 fixed point.\n            // We do this by multiplying by 2**96 / 10**18. But since\n            // ln(x * C) = ln(x) + ln(C), we can simply do nothing here\n            // and add ln(2**96 / 10**18) at the end.\n\n            // Reduce range of x to (1, 2) * 2**96\n            // ln(2^k * x) = k * ln(2) + ln(x)\n            int256 k = int256(log2(uint256(x))) - 96;\n            x <<= uint256(159 - k);\n            x = int256(uint256(x) >> 159);\n\n            // Evaluate using a (8, 8)-term rational approximation.\n            // p is made monic, we will multiply by a scale factor later.\n            int256 p = x + 3273285459638523848632254066296;\n            p = ((p * x) >> 96) + 24828157081833163892658089445524;\n            p = ((p * x) >> 96) + 43456485725739037958740375743393;\n            p = ((p * x) >> 96) - 11111509109440967052023855526967;\n            p = ((p * x) >> 96) - 45023709667254063763336534515857;\n            p = ((p * x) >> 96) - 14706773417378608786704636184526;\n            p = p * x - (795164235651350426258249787498 << 96);\n\n            // We leave p in 2**192 basis so we don't need to scale it back up for the division.\n            // q is monic by convention.\n            int256 q = x + 5573035233440673466300451813936;\n            q = ((q * x) >> 96) + 71694874799317883764090561454958;\n            q = ((q * x) >> 96) + 283447036172924575727196451306956;\n            q = ((q * x) >> 96) + 401686690394027663651624208769553;\n            q = ((q * x) >> 96) + 204048457590392012362485061816622;\n            q = ((q * x) >> 96) + 31853899698501571402653359427138;\n            q = ((q * x) >> 96) + 909429971244387300277376558375;\n            assembly {\n                // Div in assembly because solidity adds a zero check despite the unchecked.\n                // The q polynomial is known not to have zeros in the domain.\n                // No scaling required because p is already 2**96 too large.\n                r := sdiv(p, q)\n            }\n\n            // r is in the range (0, 0.125) * 2**96\n\n            // Finalization, we need to:\n            // * multiply by the scale factor s = 5.549…\n            // * add ln(2**96 / 10**18)\n            // * add k * ln(2)\n            // * multiply by 10**18 / 2**96 = 5**18 >> 78\n\n            // mul s * 5e18 * 2**96, base is now 5**18 * 2**192\n            r *= 1677202110996718588342820967067443963516166;\n            // add ln(2) * k * 5e18 * 2**192\n            r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k;\n            // add ln(2**96 / 10**18) * 5e18 * 2**192\n            r += 600920179829731861736702779321621459595472258049074101567377883020018308;\n            // base conversion: mul 2**18 / 2**192\n            r >>= 174;\n        }\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                    LOW LEVEL FIXED POINT OPERATIONS\n    //////////////////////////////////////////////////////////////*/\n\n    function mulDivDown(\n        uint256 x,\n        uint256 y,\n        uint256 denominator\n    ) internal pure returns (uint256 z) {\n        assembly {\n            // Store x * y in z for now.\n            z := mul(x, y)\n\n            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))\n            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {\n                revert(0, 0)\n            }\n\n            // Divide z by the denominator.\n            z := div(z, denominator)\n        }\n    }\n\n    function mulDivUp(\n        uint256 x,\n        uint256 y,\n        uint256 denominator\n    ) internal pure returns (uint256 z) {\n        assembly {\n            // Store x * y in z for now.\n            z := mul(x, y)\n\n            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))\n            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {\n                revert(0, 0)\n            }\n\n            // First, divide z - 1 by the denominator and add 1.\n            // We allow z - 1 to underflow if z is 0, because we multiply the\n            // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n            z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n        }\n    }\n\n    function rpow(\n        uint256 x,\n        uint256 n,\n        uint256 scalar\n    ) internal pure returns (uint256 z) {\n        assembly {\n            switch x\n            case 0 {\n                switch n\n                case 0 {\n                    // 0 ** 0 = 1\n                    z := scalar\n                }\n                default {\n                    // 0 ** n = 0\n                    z := 0\n                }\n            }\n            default {\n                switch mod(n, 2)\n                case 0 {\n                    // If n is even, store scalar in z for now.\n                    z := scalar\n                }\n                default {\n                    // If n is odd, store x in z for now.\n                    z := x\n                }\n\n                // Shifting right by 1 is like dividing by 2.\n                let half := shr(1, scalar)\n\n                for {\n                    // Shift n right by 1 before looping to halve it.\n                    n := shr(1, n)\n                } n {\n                    // Shift n right by 1 each iteration to halve it.\n                    n := shr(1, n)\n                } {\n                    // Revert immediately if x ** 2 would overflow.\n                    // Equivalent to iszero(eq(div(xx, x), x)) here.\n                    if shr(128, x) {\n                        revert(0, 0)\n                    }\n\n                    // Store x squared.\n                    let xx := mul(x, x)\n\n                    // Round to the nearest number.\n                    let xxRound := add(xx, half)\n\n                    // Revert if xx + half overflowed.\n                    if lt(xxRound, xx) {\n                        revert(0, 0)\n                    }\n\n                    // Set x to scaled xxRound.\n                    x := div(xxRound, scalar)\n\n                    // If n is even:\n                    if mod(n, 2) {\n                        // Compute z * x.\n                        let zx := mul(z, x)\n\n                        // If z * x overflowed:\n                        if iszero(eq(div(zx, x), z)) {\n                            // Revert if x is non-zero.\n                            if iszero(iszero(x)) {\n                                revert(0, 0)\n                            }\n                        }\n\n                        // Round to the nearest number.\n                        let zxRound := add(zx, half)\n\n                        // Revert if zx + half overflowed.\n                        if lt(zxRound, zx) {\n                            revert(0, 0)\n                        }\n\n                        // Return properly scaled zxRound.\n                        z := div(zxRound, scalar)\n                    }\n                }\n            }\n        }\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        GENERAL NUMBER UTILITIES\n    //////////////////////////////////////////////////////////////*/\n\n    function sqrt(uint256 x) internal pure returns (uint256 z) {\n        assembly {\n            let y := x // We start y at x, which will help us make our initial estimate.\n\n            z := 181 // The \"correct\" value is 1, but this saves a multiplication later.\n\n            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad\n            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.\n\n            // We check y >= 2^(k + 8) but shift right by k bits\n            // each branch to ensure that if x >= 256, then y >= 256.\n            if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n                y := shr(128, y)\n                z := shl(64, z)\n            }\n            if iszero(lt(y, 0x1000000000000000000)) {\n                y := shr(64, y)\n                z := shl(32, z)\n            }\n            if iszero(lt(y, 0x10000000000)) {\n                y := shr(32, y)\n                z := shl(16, z)\n            }\n            if iszero(lt(y, 0x1000000)) {\n                y := shr(16, y)\n                z := shl(8, z)\n            }\n\n            // Goal was to get z*z*y within a small factor of x. More iterations could\n            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).\n            // We ensured y >= 256 so that the relative difference between y and y+1 is small.\n            // That's not possible if x < 256 but we can just verify those cases exhaustively.\n\n            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.\n            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.\n            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.\n\n            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range\n            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.\n\n            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate\n            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.\n\n            // There is no overflow risk here since y < 2^136 after the first branch above.\n            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.\n\n            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n\n            // If x+1 is a perfect square, the Babylonian method cycles between\n            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.\n            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division\n            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.\n            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.\n            z := sub(z, lt(div(x, z), z))\n        }\n    }\n\n    function log2(uint256 x) internal pure returns (uint256 r) {\n        require(x > 0, \"UNDEFINED\");\n\n        assembly {\n            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))\n            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))\n            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n            r := or(r, shl(4, lt(0xffff, shr(r, x))))\n            r := or(r, shl(3, lt(0xff, shr(r, x))))\n            r := or(r, shl(2, lt(0xf, shr(r, x))))\n            r := or(r, shl(1, lt(0x3, shr(r, x))))\n            r := or(r, lt(0x1, shr(r, x)))\n        }\n    }\n\n    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        assembly {\n            // z will equal 0 if y is 0, unlike in Solidity where it will revert.\n            z := mod(x, y)\n        }\n    }\n\n    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        assembly {\n            // z will equal 0 if y is 0, unlike in Solidity where it will revert.\n            z := div(x, y)\n        }\n    }\n\n    /// @dev Will return 0 instead of reverting if y is zero.\n    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        assembly {\n            // Add 1 to x * y if x % y > 0.\n            z := add(gt(mod(x, y), 0), div(x, y))\n        }\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "hardhat-deploy/=node_modules/hardhat-deploy/",
        "hardhat/=node_modules/hardhat/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "forge-std/=lib/forge-std/src/",
        "solmate/=lib/solmate/src/",
        "solmate/=lib/solmate/src/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "shanghai",
      "viaIR": false,
      "libraries": {}
    }
  }}