Token

Overview CRC1155

Total Supply:
0 N/A

Holders:
186 addresses
Balance
0 N/A
0x85347f1d0257a4bdf7f482eab3a5202c6d6f6886
Loading
[ Download CSV Export  ] 
Loading
Loading

Click here to update the token ICO / general information
# Exchange Pair Price  24H Volume % Volume
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
VaultHealer

Compiler Version
v0.8.14+commit.80d49f37

Optimization Enabled:
Yes with 255 runs

Other Settings:
default evmVersion
File 1 of 41 : VaultHealer.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.14;

import "./QuartzUniV2Zap.sol";
import "./VaultHealerBoostedPools.sol";
import "@openzeppelin/contracts/utils/Multicall.sol";

contract VaultHealer is VaultHealerBoostedPools, Multicall {

    address immutable public zap;

    constructor(address _vhAuth, address _feeMan, address _zap)
        VaultHealerBase(_vhAuth, _feeMan)
        ERC1155("")
    {
        zap = _zap;
    }

   function isApprovedForAll(address account, address operator) public view override(ERC1155, IERC1155) returns (bool) {
        return super.isApprovedForAll(account, operator) || operator == address(zap);
   }

    function setURI(string calldata _uri) external auth {
        _setURI(_uri);
    }

    function stakedWantTokens(address account, uint vid) public view returns (uint) {
        uint shares = account == address(0) ? 0 : balanceOf(account, vid);
        return shares == 0 ? 0 : wantLockedTotal(vid) * shares / totalSupply[vid];
    }
    
    function tokenData(address account, uint[] calldata vids) external view returns (uint[4][] memory data) {
        /*[0]: user staked want tokens
          [1]: user shares (balanceOf)
          [2]: wantLockedTotal
          [3]: totalSupply
        */

        uint len = vids.length;
        data = new uint[4][](len);

        for (uint i; i < vids.length; i++) {
            uint vid = vids[i];
            uint supply = totalSupply[vid];
            if (supply == 0) continue;
            data[i][3] = supply;
            uint wlt = wantLockedTotal(vid);
            data[i][2] = wlt;
            if (account == address(0)) continue;
            uint bal = balanceOf(account, vid);
            data[i][1] = bal;
            data[i][0] = bal * wlt / supply;
        }
    }
    function wantLockedTotal(uint vid) public view returns (uint) {
        return strat(vid).wantLockedTotal();
    }

    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155, IERC165) returns (bool) {
        return ERC1155.supportsInterface(interfaceId) || interfaceId == type(IVaultHealer).interfaceId;
    }

    //This contract should not hold ERC20 tokens at the end of a transaction. If this happens due to some error, this will send the 
    //tokens to the treasury if it is set. Contact the team for help, and maybe they can return your missing token!
    function rescue(address token) external {
        (address receiver,) = vaultFeeManager.getWithdrawFee(0);
        if (receiver == address(0)) receiver = msg.sender;
        IERC20(token).transfer(receiver, IERC20(token).balanceOf(address(this)));
    }
}

File 2 of 41 : QuartzUniV2Zap.sol
// SPDX-License-Identifier: GPLv2

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// @author Wivern for Beefy.Finance, ToweringTopaz for Crystl.Finance
// @notice This contract adds liquidity to Uniswap V2 compatible liquidity pair pools and stake.

pragma solidity ^0.8.14;

import "./libraries/LibQuartz.sol";

contract QuartzUniV2Zap {
    using SafeERC20 for IERC20;
    using LibQuartz for IVaultHealer;
    using VaultChonk for IVaultHealer;

    uint256 public constant MINIMUM_AMOUNT = 1000;
    IVaultHealer public immutable vaultHealer;

    mapping(IERC20 => bool) private approvals;

    constructor(address _vaultHealer) {
        vaultHealer = IVaultHealer(_vaultHealer);
    }

    receive() external payable {
        require(Address.isContract(msg.sender));
    }

    function quartzInETH (uint vid, uint256 tokenAmountOutMin) external payable {
        require(msg.value >= MINIMUM_AMOUNT, 'Quartz: Insignificant input amount');
        
        IWETH weth = vaultHealer.getRouter(vid).WETH();
        
        weth.deposit{value: msg.value}();

        _swapAndStake(vid, tokenAmountOutMin, weth);
    }

    function estimateSwap(uint vid, IERC20 tokenIn, uint256 fullInvestmentIn) external view returns(uint256 swapAmountIn, uint256 swapAmountOut, IERC20 swapTokenOut) {
        return LibQuartz.estimateSwap(vaultHealer, vid, tokenIn, fullInvestmentIn);
    }

    function quartzIn (uint vid, uint256 tokenAmountOutMin, IERC20 tokenIn, uint256 tokenInAmount) external {
        uint allowance = tokenIn.allowance(msg.sender, address(this));
        uint balance = tokenIn.balanceOf(msg.sender);

        if (tokenInAmount == type(uint256).max) tokenInAmount = allowance < balance ? allowance : balance;
        else {
            require(allowance >= tokenInAmount, 'Quartz: Input token is not approved');
            require(balance >= tokenInAmount, 'Quartz: Input token has insufficient balance');
        }
        require(tokenInAmount >= MINIMUM_AMOUNT, 'Quartz: Insignificant input amount');
        
        tokenIn.safeTransferFrom(msg.sender, address(this), tokenInAmount);
        require(tokenIn.balanceOf(address(this)) >= tokenInAmount, 'Quartz: Fee-on-transfer/reflect tokens not yet supported');

        _swapAndStake(vid, tokenAmountOutMin, tokenIn);
    }

    //should only happen when this contract deposits as a maximizer
    function onERC1155Received(
        address operator, address /*from*/, uint256 /*id*/, uint256 /*amount*/, bytes calldata) external view returns (bytes4) {
        //if (msg.sender != address(vaultHealer)) revert("Quartz: Incorrect ERC1155 issuer");
        if (operator != address(this)) revert("Quartz: Improper ERC1155 transfer"); 
        return 0xf23a6e61;
    }

    function quartzOut (uint vid, uint256 withdrawAmount) public {
        (IUniRouter router,, IUniPair pair, bool isPair) = vaultHealer.getRouterAndPair(vid);
        if (withdrawAmount > 0) {
            uint[4] memory data = vaultHealer.tokenData(msg.sender, asSingletonArray(vid))[0];
            vaultHealer.safeTransferFrom(
                msg.sender, 
                address(this), 
                vid, 
                withdrawAmount > data[0] ? //user want tokens
                    data[1] : //user shares
                    withdrawAmount * data[3] / data[2], //amt * totalShares / wantLockedTotal
                ""
            );
        } else if (vaultHealer.balanceOf(address(this), vid) == 0) return;

        vaultHealer.withdraw(vid, type(uint).max, "");
        if (vid > 2**16) quartzOut(vid >> 16, 0);

        IWETH weth = router.WETH();

        if (isPair) {
            IERC20 token0 = pair.token0();
            IERC20 token1 = pair.token1();
            if (token0 != weth && token1 != weth) {
                LibQuartz.removeLiquidity(pair, msg.sender);
            } else {
                LibQuartz.removeLiquidity(pair, address(this));
                returnAsset(token0, weth); //returns any leftover tokens to user
                returnAsset(token1, weth); //returns any leftover tokens to user
            }
        } else {
            returnAsset(pair, weth);
        }
    }

    function _swapAndStake(uint vid, uint256 tokenAmountOutMin, IERC20 tokenIn) private {
        (IUniRouter router,,IUniPair pair, bool isPair) = vaultHealer.getRouterAndPair(vid);        
        
        IWETH weth = router.WETH();

        if (isPair) {
            IERC20 token0 = pair.token0();
            IERC20 token1 = pair.token1();

        //_approveTokenIfNeeded(tokenIn, router);

            if (token0 == tokenIn) {
                (uint256 reserveA, uint256 reserveB,) = pair.getReserves();
                LibQuartz.swapDirect(router, LibQuartz.getSwapAmount(router, tokenIn.balanceOf(address(this)), reserveA, reserveB), tokenIn, token1, tokenAmountOutMin);
            } else if (token1 == tokenIn) {
                (uint256 reserveA, uint256 reserveB,) = pair.getReserves();
                LibQuartz.swapDirect(router, LibQuartz.getSwapAmount(router, tokenIn.balanceOf(address(this)), reserveB, reserveA), tokenIn, token0, tokenAmountOutMin);
            } else {
                uint swapAmountIn = tokenIn.balanceOf(address(this))/2;
                
                if(LibQuartz.hasSufficientLiquidity(token0, tokenIn, router, MINIMUM_AMOUNT)) {
                    LibQuartz.swapDirect(router, swapAmountIn, tokenIn, token0, tokenAmountOutMin);
                } else {
                    LibQuartz.swapViaToken(router, swapAmountIn, tokenIn, weth, token0, tokenAmountOutMin);
                }
                
                if(LibQuartz.hasSufficientLiquidity(token1, tokenIn, router, MINIMUM_AMOUNT)) {
                    LibQuartz.swapDirect(router, swapAmountIn, tokenIn, token1, tokenAmountOutMin);
                } else {
                    LibQuartz.swapViaToken(router, swapAmountIn, tokenIn, weth, token1, tokenAmountOutMin);
                }

                returnAsset(tokenIn, weth);
            }
            
            LibQuartz.optimalMint(pair, token0, token1);
            returnAsset(token0, weth);
            returnAsset(token1, weth);
        } else {
            uint swapAmountIn = tokenIn.balanceOf(address(this));
            if(LibQuartz.hasSufficientLiquidity(pair, tokenIn, router, MINIMUM_AMOUNT)) {
                LibQuartz.swapDirect(router, swapAmountIn, tokenIn, pair, tokenAmountOutMin);
            } else {
                LibQuartz.swapViaToken(router, swapAmountIn, tokenIn, weth, pair, tokenAmountOutMin);
            }
            returnAsset(tokenIn, weth);
        }

        _approveTokenIfNeeded(pair);
        uint balance = pair.balanceOf(address(this));
        vaultHealer.deposit(vid, balance, "");
        
        balance = vaultHealer.balanceOf(address(this), vid);
        vaultHealer.safeTransferFrom(address(this), msg.sender, vid, balance, "");
    }


    function returnAsset(IERC20 token, IWETH weth) internal {
        uint256 balance = token.balanceOf(address(this));
        if (balance == 0) return;
        
        if (token == weth) {
            weth.withdraw(balance);
            (bool success,) = msg.sender.call{value: address(this).balance}(new bytes(0));
            require(success, 'Quartz: ETH transfer failed');
        } else {
            token.safeTransfer(msg.sender, balance);
        }
    }

    function _approveTokenIfNeeded(IERC20 token) private {
        if (!approvals[token]) {
            token.safeApprove(address(vaultHealer), type(uint256).max);
            approvals[token] = true;
        }
    }

    function asSingletonArray(uint256 n) internal pure returns (uint256[] memory tempArray) {
        tempArray = new uint256[](1);
        tempArray[0] = n;
    }

    //This contract should not hold ERC20 tokens at the end of a transaction. If this happens due to some error, this will send the 
    //tokens to the treasury if it is set. Contact the team for help, and maybe they can return your missing token!
    function rescue(IERC20 token) external {
        (address receiver,) = vaultHealer.vaultFeeManager().getWithdrawFee(0);
        if (receiver == address(0)) receiver = msg.sender;
        token.transfer(receiver, token.balanceOf(address(this)));
    }

}

File 3 of 41 : VaultHealerBoostedPools.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.14;

import "@openzeppelin/contracts/utils/structs/BitMaps.sol";
import "./VaultHealerGate.sol";
import "./interfaces/IBoostPool.sol";

abstract contract VaultHealerBoostedPools is VaultHealerGate {
    using BitMaps for BitMaps.BitMap;

    BitMaps.BitMap activeBoosts;
    mapping(address => BitMaps.BitMap) userBoosts;
    
    function boostPool(uint _boostID) public view returns (IBoostPool) {
        return IBoostPool(Cavendish.computeAddress(bytes32(_boostID)));
    }
    
    function nextBoostPool(uint vid) external view returns (uint, IBoostPool) {
        return boostPoolVid(vid, vaultInfo[vid].numBoosts);
    }

    function boostPoolVid(uint vid, uint16 n) public view returns (uint, IBoostPool) {

        uint _boostID = (uint(bytes32(bytes4(0xB0057000 + n))) | vid);
        return (_boostID, boostPool(_boostID));
    }

    function createBoost(uint vid, address _implementation, bytes calldata initdata) external requireValidVid(vid) auth {
        VaultChonk.createBoost(vaultInfo, activeBoosts, vid, _implementation, initdata);
    }

    //Users can enableBoost to opt-in to a boosted vault
    function enableBoost(address _user, uint _boostID) public nonReentrant {
		
        if (msg.sender != _user && !isApprovedForAll(_user, msg.sender)) revert NotApprovedToEnableBoost(_user, msg.sender);
        if (!activeBoosts.get(_boostID)) revert BoostPoolNotActive(_boostID);
        if (userBoosts[_user].get(_boostID)) revert BoostPoolAlreadyJoined(_user, _boostID);
        userBoosts[_user].set(_boostID);

        boostPool(_boostID).joinPool(_user, uint112(balanceOf(_user, uint224(_boostID))));
        emit EnableBoost(_user, _boostID);
    }

    //Standard opt-in function users will call
    function enableBoost(uint _boostID) external {
        enableBoost(msg.sender, _boostID);
    }

    function harvestBoost(uint _boostID) external nonReentrant {
        boostPool(_boostID).harvest(msg.sender);
    }

    //In case of a buggy boost pool, users can opt out at any time but lose the boost rewards
    function emergencyBoostWithdraw(uint _boostID) external nonReentrant {
        if (!userBoosts[msg.sender].get(_boostID)) revert BoostPoolNotJoined(msg.sender, _boostID);
        try boostPool(_boostID).emergencyWithdraw{gas: 2**19}(msg.sender) returns (bool success) {
            if (!success) activeBoosts.unset(_boostID); //Disable boost if the pool is broken
        } catch {
            activeBoosts.unset(_boostID);
        }
        userBoosts[msg.sender].unset(_boostID);
        emit BoostEmergencyWithdraw(msg.sender, _boostID);
    }

    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override {
        
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
        //If boosted pools are affected, update them
        
        for (uint i; i < ids.length; i++) {
            uint vid = ids[i];
            uint numBoosts = vaultInfo[vid].numBoosts;
            for (uint16 k; k < numBoosts; k++) { //Loop through all of the transferred token's boostpools (if any)
                updateBoostPool(vid, k, from, to, amounts[i]);
            }
        }
        
    }

    function updateBoostPool(uint vid, uint16 n, address from, address to, uint amount) private {
        (uint boostID, IBoostPool pool) = boostPoolVid(vid, n); //calculate address and ID for pool
        from = from == address(0) || userBoosts[from].get(boostID) ? from : address(0);
        to = to == address(0) || userBoosts[to].get(boostID) ? to : address(0);

        if ((from != address(0) || to != address(0)) && pool.notifyOnTransfer(from, to, amount)) {// Is the pool closed?
            activeBoosts.unset(boostID); //close finished pool
            userBoosts[from].unset(boostID); //pool finished for "from"
            userBoosts[to].unset(boostID); //pool finished for "to"
        }
    }

    function boostInfo(address account, uint vid) external view returns (
        BoostInfo[] memory active, //user is in these pools; pools are still earning
        BoostInfo[] memory finished, //user is in these pools; pool is finished so user needs to make final harvest
        BoostInfo[] memory available //user could join these pools and earn
    ) {
        BoostInfo[][3] memory boosts = VaultChonk.boostInfo(vaultInfo[vid].numBoosts, activeBoosts, userBoosts[account], account, vid);
        (active, finished, available) = (boosts[0], boosts[1], boosts[2]);
    }
}

File 4 of 41 : Multicall.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)

pragma solidity ^0.8.0;

import "./Address.sol";

/**
 * @dev Provides a function to batch together multiple calls in a single external call.
 *
 * _Available since v4.1._
 */
abstract contract Multicall {
    /**
     * @dev Receives and executes a batch of function calls on this contract.
     */
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            results[i] = Address.functionDelegateCall(address(this), data[i]);
        }
        return results;
    }
}

File 5 of 41 : LibQuartz.sol
// SPDX-License-Identifier: GPLv2

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// @author Wivern for Beefy.Finance, ToweringTopaz for Crystl.Finance
// @notice This contract adds liquidity to Uniswap V2 compatible liquidity pair pools and stake.

pragma solidity ^0.8.14;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./VaultChonk.sol";
import "../interfaces/IUniRouter.sol";

library LibQuartz {
    using SafeERC20 for IERC20;
    using SafeERC20 for IUniPair;
    using VaultChonk for IVaultHealer;

    uint256 constant MINIMUM_AMOUNT = 1000;
    
    function getRouter(IVaultHealer vaultHealer, uint vid) internal view returns (IUniRouter) {
        return vaultHealer.strat(vid).router();
    }
    
    function getRouterAndPair(IVaultHealer vaultHealer, uint _vid) internal view returns (IUniRouter router, IStrategy strat, IUniPair pair, bool valid) {
        strat = vaultHealer.strat(_vid);
        router = strat.router();
        pair = IUniPair(address(strat.wantToken()));

        try pair.factory() returns (IUniFactory _f) {
            valid = _f == router.factory();
            require(valid, "Quartz: This vault cannot be zapped"); //Risk of illiquid pair loss here, so we shouldn't zap
        } catch {

        }
    }
    function getSwapAmount(IUniRouter router, uint256 investmentA, uint256 reserveA, uint256 reserveB) internal pure returns (uint256 swapAmount) {
        uint256 halfInvestment = investmentA / 2;
        uint256 numerator = router.getAmountOut(halfInvestment, reserveA, reserveB);
        uint256 denominator = router.quote(halfInvestment, reserveA + halfInvestment, reserveB - numerator);
        swapAmount = investmentA - sqrt(halfInvestment * halfInvestment * numerator / denominator);
    }
    function returnAssets(IUniRouter router, IERC20[] memory tokens) internal {
        IWETH weth = router.WETH();
        
        
        for (uint256 i; i < tokens.length; i++) {
            uint256 balance = tokens[i].balanceOf(address(this));
            if (balance == 0) continue;
            if (tokens[i] == weth) {
                weth.withdraw(balance);
                (bool success,) = msg.sender.call{value: balance}(new bytes(0));
                require(success, 'Quartz: ETH transfer failed');
            } else {
                tokens[i].safeTransfer(msg.sender, balance);
            }
        }
    
    }

    function swapDirect(
        IUniRouter _router,
        uint256 _amountIn,
        IERC20 input,
        IERC20 output,
        uint amountOutMin
    ) public returns (uint amountOutput) {
        IUniFactory factory = _router.factory();

        IUniPair pair = factory.getPair(input, output);
        input.safeTransfer(address(pair), _amountIn);
        uint balanceBefore = output.balanceOf(address(this));

        bool inputIsToken0 = input < output;
        
        (uint reserve0, uint reserve1,) = pair.getReserves();

        (uint reserveInput, uint reserveOutput) = inputIsToken0 ? (reserve0, reserve1) : (reserve1, reserve0);
        uint amountInput = input.balanceOf(address(pair)) - reserveInput;
        amountOutput = _router.getAmountOut(amountInput, reserveInput, reserveOutput);

        (uint amount0Out, uint amount1Out) = inputIsToken0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
        
        pair.swap(amount0Out, amount1Out, address(this), "");
    
        if (output.balanceOf(address(this)) <= amountOutMin + balanceBefore) {
            unchecked {
                revert IStrategy.InsufficientOutputAmount(output.balanceOf(address(this)) - balanceBefore, amountOutMin);
            }
        }
    }

    function swapViaToken(
        IUniRouter _router,
        uint256 _amountIn,
        IERC20 input,
        IERC20 middle,
        IERC20 output,
        uint amountOutMin
    ) public returns (uint amountOutput) {
        IUniFactory factory = _router.factory();

        IUniPair pairA = factory.getPair(input, middle);
        IUniPair pairB = factory.getPair(middle, output);        
        input.safeTransfer(address(pairA), _amountIn);

        uint balanceBefore = output.balanceOf(address(this));

        {
            {
                (uint reserve0, uint reserve1,) = pairA.getReserves();        
                (uint reserveInput, uint reserveOutput) = (input < middle) ? (reserve0, reserve1) : (reserve1, reserve0);
                uint amountInput = input.balanceOf(address(pairA)) - reserveInput;
                amountOutput = _router.getAmountOut(amountInput, reserveInput, reserveOutput);
            }
            (uint amount0Out, uint amount1Out) = (input < middle) ? (uint(0), amountOutput) : (amountOutput, uint(0));
            pairA.swap(amount0Out, amount1Out, address(pairB), "");
        }
        {
            {
                (uint reserve0, uint reserve1,) = pairB.getReserves();
                (uint reserveInput, uint reserveOutput) = (middle < output) ? (reserve0, reserve1) : (reserve1, reserve0);
                uint amountInput = middle.balanceOf(address(pairB)) - reserveInput;
                amountOutput = _router.getAmountOut(amountInput, reserveInput, reserveOutput);
            }

            (uint amount0Out, uint amount1Out) = (middle < output) ? (uint(0), amountOutput) : (amountOutput, uint(0));
            pairB.swap(amount0Out, amount1Out, address(this), "");
        }

        if (output.balanceOf(address(this)) <= amountOutMin + balanceBefore) {
            unchecked {
                revert IStrategy.InsufficientOutputAmount(output.balanceOf(address(this)) - balanceBefore, amountOutMin);
            }
        }
    }

    function estimateSwap(IVaultHealer vaultHealer, uint pid, IERC20 tokenIn, uint256 fullInvestmentIn) public view returns(uint256 swapAmountIn, uint256 swapAmountOut, IERC20 swapTokenOut) {
        (IUniRouter router,,IUniPair pair,bool isPair) = getRouterAndPair(vaultHealer, pid);
        
        require(isPair, "Quartz: Cannot estimate swap for non-LP token");

        IERC20 token0 = pair.token0();

        (uint256 reserveA, uint256 reserveB,) = pair.getReserves();
        if (token0 == tokenIn) {
            swapTokenOut = pair.token1();
        } else {
            require(pair.token1() == tokenIn, 'Quartz: Input token not present in liquidity pair');
            swapTokenOut = token0;
            (reserveA, reserveB) = (reserveB, reserveA);
        }

        swapAmountIn = getSwapAmount(router, fullInvestmentIn, reserveA, reserveB);
        swapAmountOut = router.getAmountOut(swapAmountIn, reserveA, reserveB);
    }

    function removeLiquidity(IUniPair pair, address to) internal {
        uint balance = pair.balanceOf(address(this));

        if (balance == 0) return;
        pair.safeTransfer(address(pair), balance);
        (uint256 amount0, uint256 amount1) = pair.burn(to);

        require(amount0 >= MINIMUM_AMOUNT, 'Quartz: INSUFFICIENT_A_AMOUNT');
        require(amount1 >= MINIMUM_AMOUNT, 'Quartz: INSUFFICIENT_B_AMOUNT');
    }

    function optimalMint(IUniPair pair, IERC20 token0, IERC20 token1) public returns (uint liquidity) {
        (token0, token1) = token0 < token1 ? (token0, token1) : (token1, token0);
        
        pair.skim(address(this));
        (uint112 reserve0, uint112 reserve1,) = pair.getReserves();
        
        uint balance0 = token0.balanceOf(address(this));
        uint balance1 = token1.balanceOf(address(this));

        if (balance0 * reserve1 < balance1 * reserve0) {
            balance1 = balance0 * reserve1 / reserve0;
        } else {
            balance0 = balance1 * reserve0 / reserve1;
        }

        token0.safeTransfer(address(pair), balance0);
        token1.safeTransfer(address(pair), balance1);
        liquidity = pair.mint(address(this));
    }

    function hasSufficientLiquidity(IERC20 token0, IERC20 token1, IUniRouter router, uint256 min_amount) internal view returns (bool hasLiquidity) {
        IUniFactory factory = router.factory();
        IUniPair pair = IUniPair(factory.getPair(token0, token1));
        if (address(pair) == address(0)) return false; //pair hasn't been created, so zero liquidity
		
        (uint256 reserveA, uint256 reserveB,) = pair.getReserves();

        return reserveA > min_amount && reserveB > min_amount;
    }

    // credit for this implementation goes to
    // https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687
    function sqrt(uint256 x) internal pure returns (uint256) {
        unchecked { //impossible for any of this to overflow
            if (x == 0) return 0;
            // this block is equivalent to r = uint256(1) << (BitMath.mostSignificantBit(x) / 2);
            // however that code costs significantly more gas
            uint256 xx = x;
            uint256 r = 1;
            if (xx >= 0x100000000000000000000000000000000) {
                xx >>= 128;
                r <<= 64;
            }
            if (xx >= 0x10000000000000000) {
                xx >>= 64;
                r <<= 32;
            }
            if (xx >= 0x100000000) {
                xx >>= 32;
                r <<= 16;
            }
            if (xx >= 0x10000) {
                xx >>= 16;
                r <<= 8;
            }
            if (xx >= 0x100) {
                xx >>= 8;
                r <<= 4;
            }
            if (xx >= 0x10) {
                xx >>= 4;
                r <<= 2;
            }
            if (xx >= 0x8) {
                r <<= 1;
            }
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1; // Seven iterations should be enough
            uint256 r1 = x / r;
            return (r < r1 ? r : r1);
        }
    }

}

File 6 of 41 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.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));
        }
    }

    /**
     * @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 7 of 41 : VaultChonk.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.14;

import "../interfaces/IVaultHealer.sol";
import "../interfaces/IBoostPool.sol";
import "./Cavendish.sol";
import "@openzeppelin/contracts/utils/structs/BitMaps.sol";

library VaultChonk {
    using BitMaps for BitMaps.BitMap;

    event AddVault(uint indexed vid);
    event AddBoost(uint indexed boostid);

    function createVault(mapping(uint => IVaultHealer.VaultInfo) storage vaultInfo, uint vid, IStrategy _implementation, bytes calldata data) external {
        addVault(vaultInfo, vid, _implementation, data);
    }
	
    function createMaximizer(mapping(uint => IVaultHealer.VaultInfo) storage vaultInfo, uint targetVid, bytes calldata data) external returns (uint vid) {
		if (targetVid >= 2**208) revert IVaultHealer.MaximizerTooDeep(targetVid);
        IVaultHealer.VaultInfo storage targetVault = vaultInfo[targetVid];
        uint16 nonce = targetVault.numMaximizers + 1;
        vid = (targetVid << 16) | nonce;
        targetVault.numMaximizers = nonce;
        addVault(vaultInfo, vid, strat(targetVid).getMaximizerImplementation(), data);
    }

    function addVault(mapping(uint => IVaultHealer.VaultInfo) storage vaultInfo, uint256 vid, IStrategy implementation, bytes calldata data) private {
        //
        if (!implementation.supportsInterface(type(IStrategy).interfaceId) //doesn't support interface
            || implementation.implementation() != implementation //is proxy
        ) revert IVaultHealer.NotStrategyImpl(implementation);
        IVaultHealer implVaultHealer = implementation.vaultHealer();
        if (address(implVaultHealer) != address(this)) revert IVaultHealer.ImplWrongHealer(implVaultHealer);

        IStrategy _strat = IStrategy(Cavendish.clone(address(implementation), bytes32(uint(vid))));
        _strat.initialize(abi.encodePacked(vid, data));
        vaultInfo[vid].want = _strat.wantToken();
        vaultInfo[vid].active = true; //uninitialized vaults are paused; this unpauses
        emit AddVault(vid);
    }

    function createBoost(mapping(uint => IVaultHealer.VaultInfo) storage vaultInfo, BitMaps.BitMap storage activeBoosts, uint vid, address _implementation, bytes calldata initdata) external {
        if (vid >= 2**224) revert IVaultHealer.MaximizerTooDeep(vid);
        IVaultHealer.VaultInfo storage vault = vaultInfo[vid];
        uint16 nonce = vault.numBoosts;
        vault.numBoosts = nonce + 1;

        uint _boostID = (uint(bytes32(bytes4(0xB0057000 + nonce))) | vid);

        IBoostPool _boost = IBoostPool(Cavendish.clone(_implementation, bytes32(_boostID)));

        _boost.initialize(msg.sender, _boostID, initdata);
        activeBoosts.set(_boostID);
        emit AddBoost(_boostID);
    }

    //Computes the strategy address for any vid based on this contract's address and the vid's numeric value
    function strat(uint vid) internal view returns (IStrategy) {
        if (vid == 0) revert IVaultHealer.VidOutOfRange(0);
        return IStrategy(Cavendish.computeAddress(bytes32(vid)));
    }

    function strat(IVaultHealer vaultHealer, uint256 vid) internal pure returns (IStrategy) {
        if (vid == 0) revert IVaultHealer.VidOutOfRange(0);
        return IStrategy(Cavendish.computeAddress(bytes32(vid), address(vaultHealer)));
    }
	
    function boostInfo(
        uint16 len,
        BitMaps.BitMap storage activeBoosts, 
        BitMaps.BitMap storage userBoosts,
        address account,
        uint vid
    ) external view returns (
        IVaultHealer.BoostInfo[][3] memory boosts //active, finished, available
    ) {
        //Create bytes array indicating status of each boost pool and total number for each status
        bytes memory statuses = new bytes(len);
        uint numActive;
        uint numFinished;
        uint numAvailable;
        for (uint16 i; i < len; i++) {
            uint id = uint(bytes32(bytes4(0xB0057000 + i))) | vid;
            bytes1 status;

            if (userBoosts.get(id)) status = 0x01; //pool active for user
            if (activeBoosts.get(id) && boostPool(id).isActive()) status |= 0x02; //pool still paying rewards
            
            if (status == 0x00) continue; //pool finished, user isn't in, nothing to do
            else if (status == 0x01) numFinished++; //user in finished pool
            else if (status == 0x02) numAvailable++; //user not in active pool
            else numActive++; //user in active pool

            statuses[i] = status;
        }

        boosts[0] = new IVaultHealer.BoostInfo[](numActive);
        boosts[1] = new IVaultHealer.BoostInfo[](numFinished);
        boosts[2] = new IVaultHealer.BoostInfo[](numAvailable);

        uint[3] memory infoIndex;

        for (uint16 i; i < len; i++) {
            uint8 status = uint8(statuses[i]);
            if (status == 0) continue; //pool is done and user isn't in
            status %= 3;
            
            (uint boostID, IBoostPool pool) = boostPoolVid(vid, i);

            IVaultHealer.BoostInfo memory info = boosts[status][infoIndex[status]++]; //reference to the output array member where we will be storing the data

            info.id = boostID;
            (info.rewardToken, info.pendingReward) = pool.pendingReward(account);
        }
    }

    function boostPool(uint _boostID) internal view returns (IBoostPool) {
        return IBoostPool(Cavendish.computeAddress(bytes32(_boostID)));
    }

    function boostPoolVid(uint vid, uint16 n) internal view returns (uint, IBoostPool) {

        uint _boostID = (uint(bytes32(bytes4(0xB0057000 + n))) | vid);
        return (_boostID, boostPool(_boostID));
    }

	function sizeOf(address _contract) external view returns (uint256 size) {
	
		assembly ("memory-safe") {
			size := extcodesize(_contract)
		}
	}

}

File 8 of 41 : IUniRouter.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

import "./IUniFactory.sol";
import "./IWETH.sol";

interface IUniRouter {
    function factory() external pure returns (IUniFactory);

    function WETH() external pure returns (IWETH);

    function addLiquidity(
        IERC20 tokenA,
        IERC20 tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        );

    function addLiquidityETH(
        IERC20 token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    )
        external
        payable
        returns (
            uint256 amountToken,
            uint256 amountETH,
            uint256 liquidity
        );

    function removeLiquidity(
        IERC20 tokenA,
        IERC20 tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityETH(
        IERC20 token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountETH);

    function removeLiquidityWithPermit(
        IERC20 tokenA,
        IERC20 tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityETHWithPermit(
        IERC20 token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountToken, uint256 amountETH);

    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        IERC20[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        IERC20[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactETHForTokens(
        uint256 amountOutMin,
        IERC20[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function swapTokensForExactETH(
        uint256 amountOut,
        uint256 amountInMax,
        IERC20[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        IERC20[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapETHForExactTokens(
        uint256 amountOut,
        IERC20[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) external pure returns (uint256 amountB);

    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountOut);

    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountIn);

    function getAmountsOut(uint256 amountIn, IERC20[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    function getAmountsIn(uint256 amountOut, IERC20[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    function removeLiquidityETHSupportingFeeOnTransferTokens(
        IERC20 token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountETH);

    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        IERC20 token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        IERC20[] calldata path,
        address to,
        uint256 deadline
    ) external;

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        IERC20[] calldata path,
        address to,
        uint256 deadline
    ) external payable;

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        IERC20[] calldata path,
        address to,
        uint256 deadline
    ) external;
}

File 9 of 41 : 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 10 of 41 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.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 functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 11 of 41 : IVaultHealer.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IStrategy.sol";
import "./IVaultFeeManager.sol";
import "@openzeppelin/contracts/access/IAccessControl.sol";
import "./IBoostPool.sol";
import "../libraries/Cavendish.sol";

///@notice Interface for the Crystl v3 Vault central contract
interface IVaultHealer is IERC1155 {

    event AddVault(uint indexed vid);

    event Paused(uint indexed vid);
    event Unpaused(uint indexed vid);

    event Deposit(address indexed account, uint256 indexed vid, uint256 amount);
    event Withdraw(address indexed from, address indexed to, uint256 indexed vid, uint256 amount);

    event Earned(uint256 indexed vid, uint256 wantLockedTotal, uint256 totalSupply);
    event AddBoost(uint indexed boostid);
    event EnableBoost(address indexed user, uint indexed boostid);
    event BoostEmergencyWithdraw(address user, uint _boostID);
    event SetAutoEarn(uint indexed vid, bool earnBeforeDeposit, bool earnBeforeWithdraw);
    event FailedEarn(uint indexed vid, string reason);
    event FailedEarnBytes(uint indexed vid, bytes reason);
    event FailedWithdrawFee(uint indexed vid, string reason);
    event FailedWithdrawFeeBytes(uint indexed vid, bytes reason);
    event MaximizerHarvest(address indexed account, uint indexed vid, uint targetShares);
	
	error PausedError(uint256 vid); //Action cannot be completed on a paused vid
	error MaximizerTooDeep(uint256 targetVid); //Too many layers of nested maximizers (13 is plenty I should hope)
	error VidOutOfRange(uint256 vid); //Specified vid does not represent an existing vault
	error PanicCooldown(uint256 expiry); //Cannot panic this vault again until specified time
	error InvalidFallback(); //The fallback function should not be called in this context
	error WithdrawZeroBalance(address from); //User attempting to withdraw from a vault when they have zero shares
	error UnauthorizedPendingDepositAmount(); //Strategy attempting to pull more tokens from the user than authorized
    error RestrictedFunction(bytes4 selector);
    error NotStrategyImpl(IStrategy implementation);
    error ImplWrongHealer(IVaultHealer implHealer); //Attempting to use a strategy configured for another VH, not this one
    error InsufficientBalance(IERC20 token, address from, uint balance, uint requested);
    error InsufficientApproval(IERC20 token, address from, uint available, uint requested);

	error NotApprovedToEnableBoost(address account, address operator);
	error BoostPoolNotActive(uint256 _boostID);
	error BoostPoolAlreadyJoined(address account, uint256 _boostID);
	error BoostPoolNotJoined(address account, uint256 _boostID);
    error ArrayMismatch(uint lenA, uint lenB);

	error ERC1167_Create2Failed();	//Low-level error with creating a strategy proxy
	error ERC1167_ImplZeroAddress(); //If attempting to deploy a strategy with a zero implementation address
	
    ///@notice This is used solely by strategies to indirectly pull ERC20 tokens.
    function executePendingDeposit(address _to, uint192 _amount) external;
    ///@notice This is used solely by maximizer strategies to deposit their earnings
    function maximizerDeposit(uint256 _vid, uint256 _wantAmt, bytes calldata _data) external payable;
    ///@notice Compounds the listed vaults. Generally only needs to be called by an optimized earn script, not frontend users. Earn is triggered automatically on deposit and withdraw by default.
    function earn(uint256[] calldata vids) external returns (uint[] memory successGas);
    function earn(uint256[] calldata vids, bytes[] calldata data) external returns (uint[] memory successGas);

////Functions for users and frontend developers are below

    ///@notice Standard withdraw for msg.sender
    function withdraw(uint256 _vid, uint256 _wantAmt, bytes calldata _data) external;

    ///@notice Withdraw with custom to account
    function withdraw(uint256 _vid, uint256 _wantAmt, address _to, bytes calldata _data) external;

    function deposit(uint256 _vid, uint256 _wantAmt, bytes calldata _data) external payable;

    function totalSupply(uint256 vid) external view returns (uint256);

    ///@notice This returns the strategy address for any vid.
    ///@dev For dapp or contract usage, it may be better to calculate strategy addresses locally. The formula is in the function Cavendish.computeAddress
    //function strat(uint256 _vid) external view returns (IStrategy);

    struct VaultInfo {
        IERC20 want;
        uint8 noAutoEarn;
        bool active; //not paused
        uint48 lastEarnBlock;
        uint16 numBoosts;
        uint16 numMaximizers; //number of maximizer vaults pointing here. For vid 0x0045, its maximizer will be 0x00450001, 0x00450002, ...
    }

    function vaultInfo(uint vid) external view returns (IERC20, uint8, bool, uint48,uint16,uint16);

    function tokenData(address account, uint[] calldata vids) external view returns (uint[4][] memory data);

    //@notice Returns the number of non-maximizer vaults, where the want token is compounded within one strategy
    function numVaultsBase() external view returns (uint16);

    ///@notice The number of shares in a maximizer's target vault pending to a user account from said maximizer
    ///@param _account Some user account
    ///@param _vid The vid of the maximizer
    ///@dev The vid of the target is implied to be _vid >> 16
	function maximizerPendingTargetShares(address _account, uint256 _vid) external view returns (uint256);

    ///@notice The balance of a user's shares in a vault, plus any pending shares from maximizers
	function totalBalanceOf(address _account, uint256 _vid) external view returns (uint256 amount);

    ///@notice Harvests a single maximizer
    ///@param _vid The vid of the maximizer vault, which deposits into some other target
	function harvestMaximizer(uint256 _vid) external;

	///@notice Harvests all maximizers earning to the specified target vid
    ///@param _vid The vid of the target vault, to which many maximizers may deposit
    function harvestTarget(uint256 _vid) external;

    ///@notice This can be used to make two or more calls to the contract as an atomic transaction.
    ///@param inputs are the standard abi-encoded function calldata with selector. This can be any external function on vaultHealer.
    //function multicall(bytes[] calldata inputs) external returns (bytes[] memory);

    struct BoostInfo {
        uint id;
        IBoostPool pool;
        IERC20 rewardToken;
        uint pendingReward;
    }

    function vaultFeeManager() external view returns (IVaultFeeManager);
}

File 12 of 41 : IBoostPool.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IBoostPool {
    function bonusEndBlock() external view returns (uint32);
    function BOOST_ID() external view returns (uint256);
    function joinPool(address _user, uint112 _amount) external;
    function harvest(address) external;
    function emergencyWithdraw(address _user) external returns (bool success);
    function notifyOnTransfer(address _from, address _to, uint256 _amount) external returns (bool poolDone);
    function initialize(address _owner, uint256 _boostID, bytes calldata initdata) external;
    function pendingReward(address _user) external view returns (IERC20 token, uint256 amount);
    function isActive() external view returns (bool);
}

File 13 of 41 : Cavendish.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.14;

/// @title Cavendish clones
/// @author ToweringTopaz
/// @notice Creates ERC-1167 minimal proxies whose addresses depend only on the salt and deployer address
/// @dev See _fallback for important instructions

library Cavendish {

/*
    Proxy init bytecode: 

    11 bytes: 602d80343434335afa15f3

    60 push1 2d       : size
    80 dup1           : size size
    34 callvalue      : 0 size size 
    34 callvalue      : 0 0 size size 
    34 callvalue      : 0 0 0 size size 
    33 caller         : caller 0 0 0 size size
    5a gas            : gas caller 0 0 0 size size
    fa staticcall     : success size
    15 iszero         : 0 size
    f3 return         : 

*/
    
	bytes11 constant PROXY_INIT_CODE = hex'602d80343434335afa15f3';	//below is keccak256(abi.encodePacked(PROXY_INIT_CODE));
    bytes32 constant PROXY_INIT_HASH = hex'577cbdbf32026552c0ae211272febcff3ea352b0c755f8f39b49856dcac71019';

	error ERC1167_Create2Failed();
	error ERC1167_ImplZeroAddress();

    /// @notice Creates an 1167-compliant minimal proxy whose address is purely a function of the deployer address and the salt
    /// @param _implementation The contract to be cloned
    /// @param salt Used to determine and calculate the proxy address
    /// @return Address of the deployed proxy
    function clone(address _implementation, bytes32 salt) internal returns (address) {
        if (_implementation == address(0)) revert ERC1167_ImplZeroAddress();
        address instance;
        assembly ("memory-safe") {
            sstore(PROXY_INIT_HASH, shl(96, _implementation)) //store at slot PROXY_INIT_HASH which should be empty
            mstore(0, PROXY_INIT_CODE)
            instance := create2(0, 0x00, 11, salt)
            sstore(PROXY_INIT_HASH, 0) 
        }
        if (instance == address(0)) revert ERC1167_Create2Failed();
        return instance;
    }
    
    //Standard function to compute a create2 address deployed by this address, but not impacted by the target implemention
    function computeAddress(bytes32 salt) internal view returns (address) {
        return computeAddress(salt, address(this));
    }

    //Standard function to compute a create2 address, but not impacted by the target implemention
    function computeAddress(
        bytes32 salt,
        address deployer
    ) internal pure returns (address) {
        bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, PROXY_INIT_HASH));
        return address(uint160(uint256(_data)));
    }
	
    /// @notice Called by the proxy constructor to provide the bytecode for the final proxy contract. 
    /// @dev Deployer contracts must call Cavendish._fallback() in their own fallback functions.
    ///      Generally compatible with contracts that use fallback functions. Simply call this at the
    ///       top of your fallback, and it will run only when needed.
    function _fallback() internal view {
        assembly ("memory-safe") {
            if iszero(extcodesize(caller())) { //will be, for a contract under construction
                let _implementation := sload(PROXY_INIT_HASH)
                if gt(_implementation, 0) {
                    mstore(0x00, 0x363d3d373d3d3d363d7300000000000000000000000000000000000000000000)
                    mstore(0x0a, _implementation)
                    mstore(0x1e, 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                    return(0x00, 0x2d) //Return to external caller, not to any internal function
                }

            }
        }
    }

}

File 14 of 41 : BitMaps.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/BitMaps.sol)
pragma solidity ^0.8.0;

/**
 * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
 * Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
 */
library BitMaps {
    struct BitMap {
        mapping(uint256 => uint256) _data;
    }

    /**
     * @dev Returns whether the bit at `index` is set.
     */
    function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        return bitmap._data[bucket] & mask != 0;
    }

    /**
     * @dev Sets the bit at `index` to the boolean `value`.
     */
    function setTo(
        BitMap storage bitmap,
        uint256 index,
        bool value
    ) internal {
        if (value) {
            set(bitmap, index);
        } else {
            unset(bitmap, index);
        }
    }

    /**
     * @dev Sets the bit at `index`.
     */
    function set(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        bitmap._data[bucket] |= mask;
    }

    /**
     * @dev Unsets the bit at `index`.
     */
    function unset(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        bitmap._data[bucket] &= ~mask;
    }
}

File 15 of 41 : IERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

File 16 of 41 : IStrategy.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.13;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IUniRouter.sol";
import "../libraries/Fee.sol";
import "../libraries/Tactics.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "./IMagnetite.sol";
import "./IVaultHealer.sol";

interface IStrategy is IERC165 {

    error Muppet(address caller);
    error IdenticalAddresses(IERC20 a, IERC20 b);
    error ZeroAddress();
    error InsufficientOutputAmount(uint amountOut, uint amountOutMin);
    error Strategy_CriticalMemoryError(uint ptr);
    error Strategy_Improper1155Deposit(address operator, address from, uint id);
    error Strategy_Improper1155BatchDeposit(address operator, address from, uint[] ids);
    error Strategy_ImproperEthDeposit(address sender, uint amount);
    error Strategy_NotVaultHealer(address sender);
    error Strategy_InitializeOnlyByProxy();
    error Strategy_ExcessiveFarmSlippage();
    error Strategy_WantLockedLoss();
	error Strategy_TotalSlippageWithdrawal(); //nothing to withdraw after slippage
	error Strategy_DustDeposit(uint256 wantAdded); //Deposit amount is insignificant after slippage
    

    function initialize (bytes calldata data) external;
    function wantToken() external view returns (IERC20); // Want address
    function wantLockedTotal() external view returns (uint256); // Total want tokens managed by strategy (vaultSharesTotal + want token balance)
	function vaultSharesTotal() external view returns (uint256); //Want tokens deposited in strategy's pool
    function earn(Fee.Data[3] memory fees, address _operator, bytes calldata _data) external returns (bool success, uint256 _wantLockedTotal); // Main want token compounding function
    
    function deposit(uint256 _wantAmt, uint256 _sharesTotal, bytes calldata _data) external payable returns (uint256 wantAdded, uint256 sharesAdded);
    function withdraw(uint256 _wantAmt, uint256 _userShares, uint256 _sharesTotal, bytes calldata _data) external returns (uint256 sharesRemoved, uint256 wantAmt);

    function panic() external;
    function unpanic() external;
    function router() external view returns (IUniRouter); // Univ2 router used by this strategy

    function vaultHealer() external view returns (IVaultHealer);
    function implementation() external view returns (IStrategy);
    function isMaximizer() external view returns (bool);
    function getMaximizerImplementation() external view returns (IStrategy);

    struct ConfigInfo {
        uint256 vid;
        IERC20 want;
        uint256 wantDust;
        address masterchef;
        uint pid;
        IUniRouter _router;
        IMagnetite _magnetite;
        IERC20[] earned;
        uint256[] earnedDust;
        uint slippageFactor;
        bool feeOnTransfer;
    }

    function configInfo() external view returns (ConfigInfo memory);
    function tactics() external view returns (Tactics.TacticsA tacticsA, Tactics.TacticsB tacticsB);
    
}

File 17 of 41 : IVaultFeeManager.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "../libraries/Fee.sol";

interface IVaultFeeManager {

    function getEarnFees(uint vid) external view returns (Fee.Data[3] memory fees);
    function getWithdrawFee(uint vid) external view returns (address receiver, uint16 rate);
    function getEarnFees(uint[] calldata vids) external view returns (Fee.Data[3][] memory fees);
    function getWithdrawFees(uint[] calldata vids) external view returns (Fee.Data[] memory fees);
}

File 18 of 41 : 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 19 of 41 : 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 20 of 41 : Fee.sol
// SPDX-License-Identifier: GPLv2
pragma solidity ^0.8.14;

import "../interfaces/IWETH.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

library Fee {
    using Fee for Data;
    using Fee for Data[3];

    type Data is uint256;

    uint256 constant FEE_MAX = 3000; // 100 = 1% : basis points

    function rate(Data _fee) internal pure returns (uint16) {
        return uint16(Data.unwrap(_fee));
    }
    function receiver(Data _fee) internal pure returns (address) {
        return address(uint160(Data.unwrap(_fee) >> 16));
    }
    function receiverAndRate(Data _fee) internal pure returns (address, uint16) {
        uint fee = Data.unwrap(_fee);
        return (address(uint160(fee >> 16)), uint16(fee));
    }
    function create(address _receiver, uint16 _rate) internal pure returns (Data) {
        return Data.wrap((uint256(uint160(_receiver)) << 16) | _rate);
    }

    function totalRate(Data[3] calldata _fees) internal pure returns (uint16 total) {
        unchecked { //overflow is impossible if Fee.Data are valid
            total = uint16(Data.unwrap(_fees[0]) + Data.unwrap(_fees[1]) + Data.unwrap(_fees[2]));
            require(total <= FEE_MAX, "Max total fee of 30%");
        }
    }
    function check(Data[3] memory _fees, uint maxTotal) internal pure returns (uint16 total) {
        unchecked { //overflow is impossible if Fee.Data are valid
            total = uint16(Data.unwrap(_fees[0]) + Data.unwrap(_fees[1]) + Data.unwrap(_fees[2]));
            require(total <= maxTotal, "Max total fee exceeded");
        }
    }

    //Token amount is all fees
    function payTokenFeeAll(Data[3] calldata _fees, IERC20 _token, uint _tokenAmt) internal {
        if (_tokenAmt == 0) return;
        uint feeTotalRate = totalRate(_fees);
        for (uint i; i < 3; i++) {
            (address _receiver, uint _rate) = Fee.receiverAndRate(_fees[i]);
            if (_receiver == address(0) || _rate == 0) break;
            SafeERC20.safeTransfer(_token, _receiver, _tokenAmt * _rate / feeTotalRate);
        }
    }
    //Amount includes fee and non-fee portions
    function payTokenFeePortion(Data[3] calldata _fees, IERC20 _token, uint _tokenAmt) internal returns (uint amtAfter) {
        if (_tokenAmt == 0) return 0;
        amtAfter = _tokenAmt;
        uint feeTotalRate = totalRate(_fees);
        uint feeTotalAmt = feeTotalRate * _tokenAmt / 10000;

        for (uint i; i < 3; i++) {
            (address _receiver, uint _rate) = Fee.receiverAndRate(_fees[i]);
            if (_receiver == address(0) || _rate == 0) break;
            uint amount = _tokenAmt * _rate / 10000;
            SafeERC20.safeTransfer(_token, _receiver, amount);
        }
        return _tokenAmt - feeTotalAmt;
    }

    //Use this if ethAmt is all fees
    function payEthAll(Data[3] calldata _fees, uint _ethAmt) internal {
        if (_ethAmt == 0) return;
        uint feeTotalRate = totalRate(_fees);
        for (uint i; i < 3; i++) {
            (address _receiver, uint _rate) = Fee.receiverAndRate(_fees[i]);
            if (_receiver == address(0) || _rate == 0) break;
            (bool success,) = _receiver.call{value: _ethAmt * _rate / feeTotalRate, gas: 0x40000}("");
            require(success, "Fee: Transfer failed");
        }
    }
    //Use this if ethAmt includes both fee and non-fee portions
    function payEthPortion(Data[3] calldata _fees, uint _ethAmt) internal returns (uint ethAfter) {
        ethAfter = _ethAmt;
        for (uint i; i < 3; i++) {
            (address _receiver, uint _rate) = Fee.receiverAndRate(_fees[i]);
            if (_receiver == address(0) || _rate == 0) break;
            uint amount = _ethAmt * _rate / 10000;
            (bool success,) = _receiver.call{value: amount, gas: 0x40000}("");
            require(success, "Fee: Transfer failed");
            ethAfter -= amount;
        }
    }
    function payWethPortion(Data[3] calldata _fees, IWETH weth, uint _wethAmt) internal returns (uint wethAfter) {
        uint feeTotalRate = totalRate(_fees);
        uint feeTotalAmt = feeTotalRate * _wethAmt / 10000;
        weth.withdraw(feeTotalAmt);
        for (uint i; i < 3; i++) {
            (address _receiver, uint _rate) = Fee.receiverAndRate(_fees[i]);
            if (_receiver == address(0) || _rate == 0) break;
            uint amount = _wethAmt * _rate / 10000;
            (bool success,) = _receiver.call{value: amount, gas: 0x40000}("");
            require(success, "Fee: Transfer failed");
        }
        return _wethAmt - feeTotalAmt;
    }

    function set(Data[3] storage _fees, address[3] memory _receivers, uint16[3] memory _rates) internal {

        uint feeTotal;
        for (uint i; i < 3; i++) {
            address _receiver = _receivers[i];
            uint16 _rate = _rates[i];
            require(_receiver != address(0) || _rate == 0, "Invalid treasury address");
            feeTotal += _rate;
            uint256 _fee = uint256(uint160(_receiver)) << 16 | _rate;
            _fees[i] = Data.wrap(_fee);
        }
        require(feeTotal <= 3000, "Max total fee of 30%");
    }

    function check(Data _fee, uint maxRate) internal pure { 
        (address _receiver, uint _rate) = _fee.receiverAndRate();
        if (_rate > 0) {
            require(_receiver != address(0), "Invalid treasury address");
            require(_rate <= maxRate, "Max withdraw fee exceeded");
        }
    }

}

File 21 of 41 : Tactics.sol
// SPDX-License-Identifier: GPLv2

pragma solidity ^0.8.14;

import "@openzeppelin/contracts/utils/Address.sol";

/// @title Tactics
/// @author ToweringTopaz
/// @notice Provides a generic method which vault strategies can use to call deposit/withdraw/balance on stakingpool or masterchef-like contracts
library Tactics {
    using Address for address;

    /*
    This library handles masterchef function call data packed as follows:

        uint256 tacticsA: 
            160: masterchef
            24: pid
            8: position of vaultSharesTotal function's returned amount within the returndata 
            32: selector for vaultSharesTotal
            32: vaultSharesTotal encoded call format

        uint256 tacticsB:
            32: deposit selector
            32: deposit encoded call format
            
            32: withdraw selector
            32: withdraw encoded call format
            
            32: harvest selector
            32: harvest encoded call format
            
            32: emergencyVaultWithdraw selector
            32: emergencyVaultWithdraw encoded call format

    Encoded calls use function selectors followed by single nibbles as follows, with the output packed to 32 bytes:
        0: end of line/null
        f: 32 bytes zero
        4: specified amount
        3: address(this)
        2: pid
    */
    type TacticsA is bytes32;
    type TacticsB is bytes32;

    function masterchef(TacticsA tacticsA) internal pure returns (address) {
        return address(bytes20(TacticsA.unwrap(tacticsA)));
    }  
    function pid(TacticsA tacticsA) internal pure returns (uint24) {
        return uint24(bytes3(TacticsA.unwrap(tacticsA) << 160));
    }  
    function vaultSharesTotal(TacticsA tacticsA) internal view returns (uint256 amountStaked) {
        uint returnvarPosition = uint8(uint(TacticsA.unwrap(tacticsA)) >> 64); //where is our vaultshares in the return data
        uint64 encodedCall = uint64(uint(TacticsA.unwrap(tacticsA)));
        if (encodedCall == 0) return 0;
        bytes memory data = _generateCall(pid(tacticsA), encodedCall, 0); //pid, vst call, 0
        data = masterchef(tacticsA).functionStaticCall(data, "Tactics: staticcall failed");
        assembly ("memory-safe") {
            amountStaked := mload(add(data, add(0x20,returnvarPosition)))
        }
    }

    function deposit(TacticsA tacticsA, TacticsB tacticsB, uint256 amount) internal {
        _doCall(tacticsA, tacticsB, amount, 192);
    }
    function withdraw(TacticsA tacticsA, TacticsB tacticsB, uint256 amount) internal {
        _doCall(tacticsA, tacticsB, amount, 128);
    }
    function harvest(TacticsA tacticsA, TacticsB tacticsB) internal {
        _doCall(tacticsA, tacticsB, 0, 64);
    }
    function emergencyVaultWithdraw(TacticsA tacticsA, TacticsB tacticsB) internal {
        _doCall(tacticsA, tacticsB, 0, 0);
    }
    function _doCall(TacticsA tacticsA, TacticsB tacticsB, uint256 amount, uint256 offset) private {
        uint64 encodedCall = uint64(uint(TacticsB.unwrap(tacticsB)) >> offset);
        if (encodedCall == 0) return;
        bytes memory generatedCall = _generateCall(pid(tacticsA), encodedCall, amount);
        masterchef(tacticsA).functionCall(generatedCall, "Tactics: call failed");
        
    }

    function _generateCall(uint24 _pid, uint64 encodedCall, uint amount) private view returns (bytes memory generatedCall) {

        generatedCall = abi.encodePacked(bytes4(bytes8(encodedCall)));

        for (bytes4 params = bytes4(bytes8(encodedCall) << 32); params != 0; params <<= 4) {
            bytes1 p = bytes1(params) & bytes1(0xf0);
            uint256 word;
            if (p == 0x20) {
                word = _pid;
            } else if (p == 0x30) {
                word = uint(uint160(address(this)));
            } else if (p == 0x40) {
                word = amount;
            } else if (p != 0xf0) {
                revert("Tactics: invalid tactic");
            }
            generatedCall = abi.encodePacked(generatedCall, word);
        }
    }
}

File 22 of 41 : IMagnetite.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IMagnetite {
    function findAndSavePath(address _router, IERC20 a, IERC20 b) external returns (IERC20[] memory path);
    function overridePath(address router, IERC20[] calldata _path) external;
}

File 23 of 41 : IUniFactory.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IUniPair.sol";

interface IUniFactory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);

    function getPair(IERC20 tokenA, IERC20 tokenB) external view returns (IUniPair pair);
    function allPairs(uint) external view returns (IUniPair pair);
    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (IUniPair pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}

File 24 of 41 : IWETH.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IWETH is IERC20 {
    function deposit() external payable;
    function withdraw(uint) external;
}

File 25 of 41 : IUniPair.sol
// SPDX-License-Identifier: GPLv2
pragma solidity >=0.5.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IUniFactory.sol";

interface IUniPair is IERC20 {
  
  function DOMAIN_SEPARATOR() external view returns (bytes32);
  function PERMIT_TYPEHASH() external pure returns (bytes32);
  function nonces(address owner) external view returns (uint);

  function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

  event Mint(address indexed sender, uint amount0, uint amount1);
  event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
  event Swap(
      address indexed sender,
      uint amount0In,
      uint amount1In,
      uint amount0Out,
      uint amount1Out,
      address indexed to
  );
  event Sync(uint112 reserve0, uint112 reserve1);

  function MINIMUM_LIQUIDITY() external pure returns (uint);
  function factory() external view returns (IUniFactory);
  function token0() external view returns (IERC20);
  function token1() external view returns (IERC20);
  function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
  function price0CumulativeLast() external view returns (uint);
  function price1CumulativeLast() external view returns (uint);
  function kLast() external view returns (uint);

  function mint(address to) external returns (uint liquidity);
  function burn(address to) external returns (uint amount0, uint amount1);
  function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
  function skim(address to) external;
  function sync() external;
}

File 26 of 41 : VaultHealerGate.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.14;

import "./VaultHealerBase.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./ERC1155.sol";

abstract contract VaultHealerGate is VaultHealerBase, ERC1155 {
    using SafeERC20 for IERC20;

    struct PendingDeposit {
        IERC20 token;
        uint96 amount0;
        address from;
        uint96 amount1;
    }
    mapping(address => mapping(uint256 => uint256)) public maximizerEarningsOffset;
    mapping(uint256 => uint256) public totalSupply;
    mapping(uint256 => uint256) public totalMaximizerEarnings;

    mapping(address => PendingDeposit) private pendingDeposits;

    //For front-end and general purpose external compounding. Returned amounts are zero on failure, or the gas cost on success
    function earn(uint256[] calldata vids) external nonReentrant returns (uint[] memory successGas) {
        Fee.Data[3][] memory fees = vaultFeeManager.getEarnFees(vids);
        uint len = vids.length;

        successGas = new uint[](len);
        for (uint i; i < len; i++) {
            uint gasBefore = gasleft();
            if (_earn(vids[i], fees[i], msg.data[0:0])) successGas[i] = gasBefore - gasleft();
        }
    }

    function earn(uint256[] calldata vids, bytes[] calldata data) external nonReentrant returns (uint[] memory successGas) {
        uint len = vids.length;
        if (data.length != len) revert ArrayMismatch(len, data.length);
        Fee.Data[3][] memory fees = vaultFeeManager.getEarnFees(vids);
        
        successGas = new uint[](len);
        for (uint i; i < len; i++) {
            uint gasBefore = gasleft();
            if (_earn(vids[i], fees[i], data[i])) successGas[i] = gasBefore - gasleft();
        }
    }

    function _earn(uint256 vid, Fee.Data[3] memory fees, bytes calldata data) internal returns (bool) {
        VaultInfo storage vault = vaultInfo[vid];
        if (paused(vid) || vault.lastEarnBlock == block.number) return false;

        vault.lastEarnBlock = uint48(block.number);
        try strat(vid).earn(fees, msg.sender, data) returns (bool success, uint256 wantLockedTotal) {
            if (success) {                
                emit Earned(vid, wantLockedTotal, totalSupply[vid]);
                return true;
            }
        } catch Error(string memory reason) {
            emit FailedEarn(vid, reason);
        } catch (bytes memory reason) {
            emit FailedEarnBytes(vid, reason);
        }
        return false;
    }
    
    //Allows maximizers to make reentrant calls, only to deposit to their target
    function maximizerDeposit(uint _vid, uint _wantAmt, bytes calldata _data) external payable whenNotPaused(_vid) {
        require(address(strat(_vid)) == msg.sender, "VH: sender does not match vid");
        totalMaximizerEarnings[_vid] += _deposit(_vid >> 16, _wantAmt, _data);
    }

    // Want tokens moved from user -> this -> Strat (compounding
    function deposit(uint256 _vid, uint256 _wantAmt, bytes calldata _data) external payable whenNotPaused(_vid) nonReentrant {
        IERC20 token = vaultInfo[_vid].want;
        uint balance = token.balanceOf(msg.sender);
        if (balance < _wantAmt) revert InsufficientBalance(token, msg.sender, balance, _wantAmt);
        uint approval = token.allowance(msg.sender, address(this));
        if (approval < _wantAmt) revert InsufficientApproval(token, msg.sender, approval, _wantAmt);

        _deposit(_vid, _wantAmt, _data);
    }

    function _deposit(uint256 _vid, uint256 _wantAmt, bytes calldata _data) private returns (uint256 vidSharesAdded) {
        uint totalSupplyBefore = totalSupply[_vid];
        // If enabled, we call an earn on the vault before we action the _deposit
        if (totalSupplyBefore > 0 && vaultInfo[_vid].noAutoEarn & 1 == 0) _earn(_vid, vaultFeeManager.getEarnFees(_vid), _data); 

        //Store the _from address, deposit amount, and ERC20 token associated with this vault. The strategy will be able to withdraw from _from via 
        //VaultHealer's approval, but no more than _wantAmt. This allows VaultHealer to be the only vault contract where token approvals are needed. 
        //Users can be approve VaultHealer freely and be assured that VaultHealer will not withdraw anything except when they call deposit, and only
        //up to the correct deposit amount.
        IERC20 vaultWant = vaultInfo[_vid].want;
        if (_wantAmt > 0 && address(vaultWant) != address(0)) pendingDeposits[address(strat(_vid))] = PendingDeposit({
            token: vaultWant,
            amount0: uint96(_wantAmt >> 96),
            from: msg.sender,
            amount1: uint96(_wantAmt)
        });

        // we make the deposit
        (_wantAmt, vidSharesAdded) = strat(_vid).deposit{value: msg.value}(_wantAmt, totalSupplyBefore, abi.encode(msg.sender, msg.sender, msg.sender, _data));

        //we mint tokens for the user via the 1155 contract
        _mint(
            msg.sender,
            _vid, //use the vid of the strategy 
            vidSharesAdded,
            _data
        );

        delete pendingDeposits[address(strat(_vid))]; //In case the pending deposit was not used, don't store it

        emit Deposit(msg.sender, _vid, _wantAmt);
    }

    // Withdraw LP tokens from MasterChef.
    function withdraw(uint256 _vid, uint256 _wantAmt, bytes calldata _data) external nonReentrant {
        _withdraw(_vid, _wantAmt, msg.sender, _data);
    }

    function withdraw(uint256 _vid, uint256 _wantAmt, address _to, bytes calldata _data) external nonReentrant {
        _withdraw(_vid, _wantAmt, _to, _data);
    }

    function _withdraw(uint256 _vid, uint256 _wantAmt, address _to, bytes calldata _data) private returns (uint256 vidSharesRemoved) {
		uint fromBalance = balanceOf(msg.sender, _vid);
        if (fromBalance == 0) revert WithdrawZeroBalance(msg.sender);

        // we call an earn on the vault before we action the _deposit
        if (vaultInfo[_vid].noAutoEarn & 2 == 0) _earn(_vid, vaultFeeManager.getEarnFees(_vid), _data); 

        (vidSharesRemoved, _wantAmt) = strat(_vid).withdraw(_wantAmt, fromBalance, totalSupply[_vid], abi.encode(msg.sender, msg.sender, _to, _data));
		
        //burn the tokens equal to vidSharesRemoved
        _burn(
            msg.sender,
            _vid,
            vidSharesRemoved
        );
		
        //Collect the withdrawal fee and transfer the ERC20 token out
        IERC20 _wantToken = vaultInfo[_vid].want;
        address vaultStrat = address(strat(_vid));
        if (address(_wantToken) != address(0)) {
            //withdraw fee is implemented here
            try vaultFeeManager.getWithdrawFee(_vid) returns (address feeReceiver, uint16 feeRate) {
                //hardcoded 3% max fee rate
                if (feeReceiver != address(0) && feeRate <= 300 && !paused(_vid)) { //waive withdrawal fee on paused vaults as there's generally something wrong
                    uint feeAmt = _wantAmt * feeRate / 10000;
                    _wantAmt -= feeAmt;
                    _wantToken.safeTransferFrom(vaultStrat, feeReceiver, feeAmt);
                }
            } catch {}

            _wantToken.safeTransferFrom(vaultStrat, _to, _wantAmt);
        }

        emit Withdraw(msg.sender, _to, _vid, _wantAmt);
    }

    //called by strategy, cannot be nonReentrant
    function executePendingDeposit(address _to, uint192 _amount) external {
        IERC20 token = pendingDeposits[msg.sender].token;
        uint amount0 = pendingDeposits[msg.sender].amount0;
        address from = pendingDeposits[msg.sender].from;
        uint amount1 = pendingDeposits[msg.sender].amount1;
        if (_amount > amount0 << 96 | amount1) revert UnauthorizedPendingDepositAmount();
        delete pendingDeposits[msg.sender];

        token.safeTransferFrom(from, _to, _amount);
    }

    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);

        if (from == address(0)) { //tokens minted during deposit
            for (uint i; i < ids.length; i++) {
                uint vid = ids[i];
                uint supplyBefore = totalSupply[vid];
                uint supplyAfter = supplyBefore + amounts[i];
                totalSupply[vid] = supplyAfter;

                if (vid > 2**16 && supplyBefore > 0) {
                    uint bal = balanceOf(to, vid);
                    _maximizerHarvest(to, vid, bal, bal + amounts[i], supplyBefore, supplyAfter);
                }
            }
        } else if (to == address(0)) { //tokens burned during withdrawal
            for (uint i; i < ids.length; i++) {
                uint vid = ids[i];
                uint amount = amounts[i];
                uint supplyAfter = totalSupply[vid] - amount;
                totalSupply[vid] = supplyAfter;

                if (vid > 2**16 && amount > 0) {
                    if (supplyAfter == 0) {
                        uint targetVid = vid >> 16;
                        address vaultStrat = address(strat(vid));
                        uint remainingTargetShares = balanceOf(vaultStrat, targetVid);

                        _safeTransferFrom(vaultStrat, from, targetVid, remainingTargetShares, "");

                        totalMaximizerEarnings[vid] = 0;
                        maximizerEarningsOffset[from][vid] = 0;
                        emit MaximizerHarvest(from, vid, remainingTargetShares);
                    } else {
                        uint bal = balanceOf(from, vid);
                        _maximizerHarvest(from, vid, bal, bal - amount, supplyAfter + amount, supplyAfter);
                    }
                }
            }
        } else {
            for (uint i; i < ids.length; i++) {
                uint vid = ids[i];
                if (vid > 2**16) {
                    _earn(vid, vaultFeeManager.getEarnFees(vid), msg.data[0:0]);
                    _maximizerHarvest(from, vid, balanceOf(from, vid), balanceOf(from, vid) - amounts[i], totalSupply[vid], totalSupply[vid]);
                    _maximizerHarvest(to, vid, balanceOf(to, vid), balanceOf(to, vid) + amounts[i], totalSupply[vid], totalSupply[vid]);
                }
            }
        }
    }

	//Add nonReentrant for maximizer security
	function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override(ERC1155, IERC1155) nonReentrant { super.safeTransferFrom(from, to, id, amount, data); }
	
	//Add nonReentrant for maximizer security
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override(ERC1155, IERC1155) nonReentrant { super.safeBatchTransferFrom(from, to, ids, amounts, data); }


    // // For maximizer vaults, this function helps us keep track of each users' claim on the tokens in the target vault
	function _maximizerHarvest(address _account, uint256 _vid, uint256 _balance, uint256 _supply) private {
        _maximizerHarvest(_account, _vid, _balance, _balance, _supply, _supply);
    }
	
	
    function _maximizerHarvest(address _account, uint256 _vid, uint256 _balanceBefore, uint256 _balanceAfter, uint256 _supplyBefore, uint256 _supplyAfter) private {
        uint accountOffset = maximizerEarningsOffset[_account][_vid];
        uint totalBefore = totalMaximizerEarnings[_vid];
		
		maximizerEarningsOffset[_account][_vid] = _balanceAfter * totalBefore / _supplyBefore;
        totalMaximizerEarnings[_vid] = _supplyAfter * totalBefore / _supplyBefore;

        uint targetShares = _balanceBefore * totalBefore / _supplyBefore;
        if (targetShares > accountOffset) {
            uint sharesEarned = targetShares - accountOffset;
            _safeTransferFrom(address(strat(_vid)), _account, _vid >> 16, sharesEarned, "");
            emit MaximizerHarvest(_account, _vid, sharesEarned);
        }
    }

	
	function maximizerPendingTargetShares(address _account, uint256 _vid) public view returns (uint256) {
        uint userVaultBalance = balanceOf(_account, _vid);
		if (userVaultBalance == 0) return 0;		

		uint targetVidShares = userVaultBalance * totalMaximizerEarnings[_vid] / totalSupply[_vid];
		uint accountOffset = maximizerEarningsOffset[_account][_vid];
		
		return targetVidShares > accountOffset ? targetVidShares - accountOffset : 0;
	}

	//balanceOf, but including all pending shares from maximizers
	function totalBalanceOf(address _account, uint256 _vid) external view returns (uint256 amount) {
		amount = super.balanceOf(_account, _vid);
		uint lastMaximizer = (_vid << 16) + vaultInfo[_vid].numMaximizers;
		for (uint i = (_vid << 16) + 1; i <= lastMaximizer; i++) {
			amount += maximizerPendingTargetShares(_account, i);
		}
	}
    function totalBalanceOfBatch(address[] calldata _account, uint256[] calldata _vid) external view returns (uint256[] memory amounts) {
        amounts = super.balanceOfBatch(_account, _vid);

        for (uint k; k < amounts.length; k++) {
            uint lastMaximizer = (_vid[k] << 16) + vaultInfo[_vid[k]].numMaximizers;
            for (uint i = (_vid[k] << 16) + 1; i <= lastMaximizer; i++) {
                amounts[k] += maximizerPendingTargetShares(_account[k], i);
            }
        }
    }

	function harvestMaximizer(uint256 _vid) external nonReentrant {
		_maximizerHarvest(msg.sender, _vid, balanceOf(msg.sender, _vid), totalSupply[_vid]);
	}
	
	function harvestTarget(uint256 _vid) external nonReentrant {
		uint lastMaximizer = (_vid << 16) + vaultInfo[_vid].numMaximizers;
		for (uint i = (_vid << 16) + 1; i <= lastMaximizer; i++) {
			_maximizerHarvest(msg.sender, i, balanceOf(msg.sender, i), totalSupply[i]);
		}		
	}
}

File 27 of 41 : VaultHealerBase.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.14;

import "./libraries/Cavendish.sol";
import "./interfaces/IVaultHealer.sol";
import "./VaultFeeManager.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./VaultHealerAuth.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "./libraries/VaultChonk.sol";

abstract contract VaultHealerBase is IVaultHealer, ReentrancyGuard {

    uint constant PANIC_LOCK_DURATION = 6 hours;

    IVaultFeeManager immutable public vaultFeeManager;
    VaultHealerAuth immutable public vhAuth;
    
    uint16 public numVaultsBase; //number of non-maximizer vaults

    mapping(uint => VaultInfo) public vaultInfo; // Info of each vault.
	mapping(uint => uint) private panicLockExpiry;

    constructor(address _vhAuth, address _feeMan) {
        vhAuth = VaultHealerAuth(_vhAuth);
        vaultFeeManager = IVaultFeeManager(_feeMan);
    }

    modifier auth {
        _auth();
        _;
    }
    function _auth() view private {
        bytes4 selector = bytes4(msg.data);
        if (!IAccessControl(vhAuth).hasRole(selector, msg.sender)) revert RestrictedFunction(selector);
    }

    function createVault(IStrategy _implementation, bytes calldata data) external auth nonReentrant returns (uint16 vid) {
        vid = numVaultsBase + 1;
        numVaultsBase = vid;
        VaultChonk.createVault(vaultInfo, vid, _implementation, data);
    }
	
    function createMaximizer(uint targetVid, bytes calldata data) external requireValidVid(targetVid) auth nonReentrant returns (uint vid) {
        return VaultChonk.createMaximizer(vaultInfo, targetVid, data);
    }

    //Computes the strategy address for any vid based on this contract's address and the vid's numeric value
    function strat(uint _vid) public view returns (IStrategy) {
        return VaultChonk.strat(_vid);
    }

    //Requires that a vid represents some deployed vault
    modifier requireValidVid(uint vid) {
        _requireValidVid(vid);
        _;
    }
    function _requireValidVid(uint vid) internal view {
        uint subVid = vid & 0xffff;
        if (subVid == 0 || subVid > (subVid == vid ? numVaultsBase : vaultInfo[vid >> 16].numMaximizers))
			revert VidOutOfRange(vid);
    }

    //True values are the default behavior; call earn before deposit/withdraw
    function setAutoEarn(uint vid, bool earnBeforeDeposit, bool earnBeforeWithdraw) external auth requireValidVid(vid) {
        vaultInfo[vid].noAutoEarn = (earnBeforeDeposit ? 0 : 1) | (earnBeforeWithdraw ? 0 : 2);
        emit SetAutoEarn(vid, earnBeforeDeposit, earnBeforeWithdraw);
    }

//Like OpenZeppelin Pausable, but centralized here at the vaulthealer

    function pause(uint vid, bool panic) external auth requireValidVid(vid) {
        if (vaultInfo[vid].active) { //use direct variable; paused(vid) also may be true due to maximizer
            if (panic) {
                uint expiry = panicLockExpiry[vid];
                if (expiry > block.timestamp) revert PanicCooldown(expiry);
                expiry = block.timestamp + PANIC_LOCK_DURATION;
                strat(vid).panic();
            }
            vaultInfo[vid].active = false;
            emit Paused(vid);
        }
    }
    function unpause(uint vid) external auth requireValidVid(vid) {
        if ((vid >> 16) > 0 && paused(vid >> 16)) revert PausedError(vid >> 16); // if maximizer's target is paused, it must be unpaused first
        if (!vaultInfo[vid].active) { //use direct variable
            vaultInfo[vid].active = true;
            strat(vid).unpanic();
            emit Unpaused(vid);
        }
    }
    function paused(uint vid) public view returns (bool) {
        return !vaultInfo[vid].active || ((vid >> 16) > 0 && paused(vid >> 16));
    }
    function paused(uint[] calldata vids) external view returns (bytes memory pausedArray) {
        
        uint len = vids.length;
        pausedArray = new bytes(len);

        for (uint i; i < len; i++) {
            pausedArray[i] = paused(vids[i]) ? bytes1(0x01) : bytes1(0x00);
        }        
    }
    modifier whenPaused(uint vid) {
        if (!paused(vid)) revert PausedError(vid);
        _;
    }
    modifier whenNotPaused(uint vid) {
        if (paused(vid)) revert PausedError(vid);
        _;
    }

    fallback() external {
        Cavendish._fallback();
        revert InvalidFallback();
    }
}

File 28 of 41 : ERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/ERC1155.sol)

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";

/**
 * @dev Implementation of the basic standard multi-token.
 * See https://eips.ethereum.org/EIPS/eip-1155
 * Originally based on code by Enjin: https://github.com/enjin/erc-1155
 *
 * _Available since v3.1._
 */
contract ERC1155 is ERC165, IERC1155, IERC1155MetadataURI {
    using Address for address;

    error ERC1155_BalanceQueryZeroAddress();
    error ERC1155_TransferZeroAddress();
    error ERC1155_ReceiverRejectedTokens();
    error ERC1155_ArrayLengthMismatch();    
    error ERC1155_NotReceiver();
    error ERC1155_SetApprovalSelf();
    error ERC1155_NotApproved();
    error ERC1155_InsufficientBalance();

    // Mapping from token ID to account balances
    mapping(uint256 => mapping(address => uint256)) private _balances;

    // Mapping from account to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
    string private _uri;

    /**
     * @dev See {_setURI}.
     */
    constructor(string memory uri_) {
        _setURI(uri_);
    }

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

    /**
     * @dev See {IERC1155MetadataURI-uri}.
     *
     * This implementation returns the same URI for *all* token types. It relies
     * on the token type ID substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * Clients calling this function must replace the `\{id\}` substring with the
     * actual token type ID.
     */
    function uri(uint256) public view virtual override returns (string memory) {
        return _uri;
    }

    /**
     * @dev See {IERC1155-balanceOf}.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
        if (account == address(0)) revert ERC1155_BalanceQueryZeroAddress();
        return _balances[id][account];
    }
    
    /**
     * @dev See {IERC1155-balanceOfBatch}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
        public
        view
        virtual
        override
        returns (uint256[] memory)
    {
        if (accounts.length != ids.length) revert ERC1155_ArrayLengthMismatch();

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }

    /**
     * @dev See {IERC1155-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(msg.sender, operator, approved);
    }

    /**
     * @dev See {IERC1155-isApprovedForAll}.
     */
    function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[account][operator];
    }

    /**
     * @dev See {IERC1155-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        if (!(from == msg.sender || isApprovedForAll(from, msg.sender))) revert ERC1155_NotApproved();
        _safeTransferFrom(from, to, id, amount, data);
    }

    /**
     * @dev See {IERC1155-safeBatchTransferFrom}.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        if (!(from == msg.sender || isApprovedForAll(from, msg.sender))) revert ERC1155_NotApproved();
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        if (to == address(0)) revert ERC1155_TransferZeroAddress();

        address operator = msg.sender;
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        uint256 fromBalance = _balances[id][from];
        if (fromBalance < amount) revert ERC1155_InsufficientBalance();
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        if (amounts.length != ids.length) revert ERC1155_ArrayLengthMismatch();
        if (to == address(0)) revert ERC1155_TransferZeroAddress();

        address operator = msg.sender;

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            if (fromBalance < amount) revert ERC1155_InsufficientBalance();
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    }

    /**
     * @dev Sets a new URI for all token types, by relying on the token type ID
     * substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * By this mechanism, any occurrence of the `\{id\}` substring in either the
     * URI or any of the amounts in the JSON file at said URI will be replaced by
     * clients with the token type ID.
     *
     * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
     * interpreted by clients as
     * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
     * for token type ID 0x4cce0.
     *
     * See {uri}.
     *
     * Because these URIs cannot be meaningfully represented by the {URI} event,
     * this function emits no events.
     */
    function _setURI(string memory newuri) internal virtual {
        _uri = newuri;
    }

    /**
     * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        if (to == address(0)) revert ERC1155_TransferZeroAddress();

        address operator = msg.sender;
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        if (to == address(0)) revert ERC1155_TransferZeroAddress();
        if (amounts.length != ids.length) revert ERC1155_ArrayLengthMismatch();

        address operator = msg.sender;

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

    /**
     * @dev Destroys `amount` tokens of token type `id` from `from`
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `from` must have at least `amount` tokens of token type `id`.
     */
    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal virtual {
        if (from == address(0)) revert ERC1155_TransferZeroAddress();

        address operator = msg.sender;
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        uint256 fromBalance = _balances[id][from];
        if (fromBalance < amount) revert ERC1155_InsufficientBalance();
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _burnBatch(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        if (from == address(0)) revert ERC1155_TransferZeroAddress();
        if (amounts.length != ids.length) revert ERC1155_ArrayLengthMismatch();

        address operator = msg.sender;

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            if (fromBalance < amount) revert ERC1155_InsufficientBalance();
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        if (owner == operator) revert ERC1155_SetApprovalSelf();
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `ids` and `amounts` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    /**
     * @dev Hook that is called after any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155Receiver.onERC1155Received.selector) {
                    revert ERC1155_ReceiverRejectedTokens();
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert ERC1155_NotReceiver();
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                bytes4 response
            ) {
                if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                    revert ERC1155_ReceiverRejectedTokens();
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert ERC1155_NotReceiver();
            }
        }
    }

    function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
        uint256[] memory array = new uint256[](1);
        array[0] = element;

        return array;
    }
}

File 29 of 41 : VaultFeeManager.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.14;

import "@openzeppelin/contracts/access/IAccessControl.sol";
import "./interfaces/IVaultHealer.sol";
import "@openzeppelin/contracts/utils/structs/BitMaps.sol";
import "./interfaces/IVaultFeeManager.sol";
import "./libraries/Constants.sol";

contract VaultFeeManager is IVaultFeeManager {
    using BitMaps for BitMaps.BitMap;
    using Fee for *;

    address constant public TX_ORIGIN = address(bytes20(keccak256("TX_ORIGIN"))); // if this address is used for earn fee, substitute tx.origin to pay the account providing the gas

    IAccessControl immutable public vhAuth;

    mapping(uint256 => Fee.Data) withdrawFee;
    mapping(uint256 => Fee.Data[3]) earnFees;

    BitMaps.BitMap internal _overrideDefaultEarnFees; // strategy's fee config doesn't change with the vaulthealer's default
    BitMaps.BitMap private _overrideDefaultWithdrawFee;
    Fee.Data[3] public defaultEarnFees; // Settings which are generally applied to all strategies
    Fee.Data public defaultWithdrawFee; //withdrawal fee is set separately from earn fees

    event SetDefaultEarnFees(Fee.Data[3] _earnFees);
    event SetDefaultWithdrawFee(Fee.Data _withdrawFee);
    event SetEarnFees(uint vid, Fee.Data[3] _earnFees);
    event SetWithdrawFee(uint vid, Fee.Data _withdrawFee);
    event ResetEarnFees(uint vid);
    event ResetWithdrawFee(uint vid);

    constructor(address _vhAuth) {
        vhAuth = IAccessControl(_vhAuth);
    }

    modifier auth {
        _auth();
        _;
    }
    function _auth() internal view virtual {
        require(vhAuth.hasRole(FEE_SETTER, msg.sender), "!auth");
    }

    function getEarnFees(uint _vid) external view returns (Fee.Data[3] memory _fees) {
        _fees = _overrideDefaultEarnFees.get(_vid) ? earnFees[_vid] : defaultEarnFees;
        for (uint i; i < 3; i++) {
            if (_fees[i].receiver() == TX_ORIGIN)
                _fees[i] = Fee.create(tx.origin, _fees[i].rate());
        }
    }

    function getEarnFees(uint[] calldata _vids) external view returns (Fee.Data[3][] memory _fees) {
        _fees = new Fee.Data[3][](_vids.length);
        Fee.Data[3] memory _default = defaultEarnFees;
        for (uint i; i < _vids.length; i++) {
            uint vid = _vids[i];
            _fees[i] = _overrideDefaultEarnFees.get(vid) ? earnFees[vid] : _default;
            for (uint k; k < 3; k++) {
                if (_fees[i][k].receiver() == TX_ORIGIN)
                    _fees[i][k] = Fee.create(tx.origin, _fees[i][k].rate());
            }
        }
    }

    function getWithdrawFee(uint _vid) external view returns (address _receiver, uint16 _rate) {
        return _overrideDefaultWithdrawFee.get(_vid) ? withdrawFee[_vid].receiverAndRate() : defaultWithdrawFee.receiverAndRate();
    }

    function getWithdrawFees(uint[] calldata _vids) external view returns (Fee.Data[] memory _withdrawFees) {
        _withdrawFees = new Fee.Data[](_vids.length);
        Fee.Data _default = defaultWithdrawFee;
        for (uint i; i < _vids.length; i++) {
            uint vid = _vids[i];
            _withdrawFees[i] = _overrideDefaultWithdrawFee.get(vid) ? withdrawFee[vid] : _default;
        }
    }

    function setDefaultWithdrawFee(address withdrawReceiver, uint16 withdrawRate) external auth {
         _setDefaultWithdrawFee(withdrawReceiver, withdrawRate);
    }
    function _setDefaultWithdrawFee(address withdrawReceiver, uint16 withdrawRate) internal {
        defaultWithdrawFee = Fee.create(withdrawReceiver, withdrawRate);
        Fee.check(defaultWithdrawFee, 300);
        emit SetDefaultWithdrawFee(defaultWithdrawFee);
    }

    function setEarnFees(uint _vid, address[3] calldata earnReceivers, uint16[3] calldata earnRates) external auth {
        _overrideDefaultEarnFees.set(_vid);
        earnFees[_vid].set(earnReceivers, earnRates);
        Fee.check(earnFees[_vid], 3000);
        emit SetEarnFees(_vid, earnFees[_vid]);
    }
    function resetEarnFees(uint _vid) external auth {
        _overrideDefaultEarnFees.unset(_vid);
        delete earnFees[_vid];
        emit ResetEarnFees(_vid);
    }
    
    function setDefaultEarnFees(address[3] memory earnReceivers, uint16[3] memory earnRates) external auth {
        _setDefaultEarnFees(earnReceivers, earnRates);
    }
    function _setDefaultEarnFees(address[3] memory earnReceivers, uint16[3] memory earnRates) internal {
        defaultEarnFees.set(earnReceivers, earnRates);
        Fee.check(defaultEarnFees, 3000);
        emit SetDefaultEarnFees(defaultEarnFees);
    }

    function setWithdrawFee(uint _vid, address withdrawReceiver, uint16 withdrawRate) external auth {
        _overrideDefaultWithdrawFee.set(_vid);
        withdrawFee[_vid] = Fee.create(withdrawReceiver, withdrawRate);
        Fee.check(defaultWithdrawFee, 300);
        emit SetWithdrawFee(_vid, withdrawFee[_vid]);
    }

    function resetWithdrawFee(uint _vid) external auth {
        _overrideDefaultEarnFees.unset(_vid);
        withdrawFee[_vid] = Fee.Data.wrap(0);
         emit ResetWithdrawFee(_vid);
    }


}

File 30 of 41 : 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() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

File 31 of 41 : VaultHealerAuth.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.14;

import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "./libraries/Constants.sol";

contract VaultHealerAuth is AccessControlEnumerable {

    bytes32 constant CREATE_ADMIN = keccak256("CREATE_ADMIN");
    bytes32 constant PAUSE_ADMIN = keccak256("PAUSE_ADMIN");

    constructor() {
        _setRoleAdmin(bytes4(keccak256("createVault(address,bytes)")), CREATE_ADMIN);
        _setRoleAdmin(bytes4(keccak256("createMaximizer(uint256,bytes)")), CREATE_ADMIN);
        _setRoleAdmin(bytes4(keccak256("createBoost(uint256,address,bytes)")), CREATE_ADMIN);
        _setRoleAdmin(bytes4(keccak256("pause(uint256,bool)")), PAUSE_ADMIN);
        _setRoleAdmin(bytes4(keccak256("setAutoEarn(uint256,bool,bool)")), PAUSE_ADMIN);
        _setRoleAdmin(bytes4(keccak256("unpause(uint256)")), PAUSE_ADMIN);
    }

    function setAccess(address account, uint level) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _setAccess(account, level);
    }

    //Sets an account's roles to match a predefined tiered list, with 3 being the highest level. These correspond to VaultHealer function selectors.
    function _setAccess(address account, uint level) internal {

        if (level > 3) revert("Invalid access level");

        function(bytes32,address) update = _revokeRole;

        if (level == 3) update = _grantRole; //Owner-level access, controlling fees and permissions

        update(DEFAULT_ADMIN_ROLE, account);
        update(CREATE_ADMIN, account);
        update(PAUSE_ADMIN, account);
        update(FEE_SETTER, account);
        update(bytes4(keccak256("setURI(string)")), account);

        if (level == 2) update = _grantRole; //Vault creators

        update(bytes4(keccak256("createVault(address,bytes)")), account);
        update(bytes4(keccak256("createMaximizer(uint256,bytes)")), account);
        update(bytes4(keccak256("createBoost(uint256,address,bytes)")), account);            

        if (level == 1) update = _grantRole; //Pausers

        update(bytes4(keccak256("setAutoEarn(uint256,bool,bool)")), account);
        update(bytes4(keccak256("unpause(uint256)")), account);
        update(bytes4(keccak256("pause(uint256,bool)")), account);

    }

}

File 32 of 41 : Constants.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.14;


bytes32 constant FEE_SETTER = keccak256("FEE_SETTER");

File 33 of 41 : AccessControlEnumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;

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

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Overload {_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override {
        super._grantRole(role, account);
        _roleMembers[role].add(account);
    }

    /**
     * @dev Overload {_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override {
        super._revokeRole(role, account);
        _roleMembers[role].remove(account);
    }
}

File 34 of 41 : IAccessControlEnumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

File 35 of 41 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.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(uint160(account), 20),
                        " 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.
     */
    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.
     */
    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`.
     */
    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.
     *
     * [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.
     */
    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.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 36 of 41 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly {
            result := store
        }

        return result;
    }
}

File 37 of 41 : 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 38 of 41 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

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

    /**
     * @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);
    }
}

File 39 of 41 : 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 40 of 41 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

File 41 of 41 : IERC1155MetadataURI.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)

pragma solidity ^0.8.0;

import "../IERC1155.sol";

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURI is IERC1155 {
    /**
     * @dev Returns the URI for token type `id`.
     *
     * If the `\{id\}` substring is present in the URI, it must be replaced by
     * clients with the actual token type ID.
     */
    function uri(uint256 id) external view returns (string memory);
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 255,
    "details": {
      "peephole": true,
      "inliner": true,
      "jumpdestRemover": true,
      "orderLiterals": true,
      "deduplicate": true,
      "cse": true,
      "constantOptimizer": true,
      "yul": true
    }
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/libraries/VaultChonk.sol": {
      "VaultChonk": "0xd40fdcd3569859ff09ea9f8070a41bd8c9b64876"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_vhAuth","type":"address"},{"internalType":"address","name":"_feeMan","type":"address"},{"internalType":"address","name":"_zap","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"lenA","type":"uint256"},{"internalType":"uint256","name":"lenB","type":"uint256"}],"name":"ArrayMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"_boostID","type":"uint256"}],"name":"BoostPoolAlreadyJoined","type":"error"},{"inputs":[{"internalType":"uint256","name":"_boostID","type":"uint256"}],"name":"BoostPoolNotActive","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"_boostID","type":"uint256"}],"name":"BoostPoolNotJoined","type":"error"},{"inputs":[],"name":"ERC1155_ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"ERC1155_BalanceQueryZeroAddress","type":"error"},{"inputs":[],"name":"ERC1155_InsufficientBalance","type":"error"},{"inputs":[],"name":"ERC1155_NotApproved","type":"error"},{"inputs":[],"name":"ERC1155_NotReceiver","type":"error"},{"inputs":[],"name":"ERC1155_ReceiverRejectedTokens","type":"error"},{"inputs":[],"name":"ERC1155_SetApprovalSelf","type":"error"},{"inputs":[],"name":"ERC1155_TransferZeroAddress","type":"error"},{"inputs":[],"name":"ERC1167_Create2Failed","type":"error"},{"inputs":[],"name":"ERC1167_ImplZeroAddress","type":"error"},{"inputs":[{"internalType":"contract IVaultHealer","name":"implHealer","type":"address"}],"name":"ImplWrongHealer","type":"error"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"}],"name":"InsufficientApproval","type":"error"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidFallback","type":"error"},{"inputs":[{"internalType":"uint256","name":"targetVid","type":"uint256"}],"name":"MaximizerTooDeep","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"NotApprovedToEnableBoost","type":"error"},{"inputs":[{"internalType":"contract IStrategy","name":"implementation","type":"address"}],"name":"NotStrategyImpl","type":"error"},{"inputs":[{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"PanicCooldown","type":"error"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"}],"name":"PausedError","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"RestrictedFunction","type":"error"},{"inputs":[],"name":"UnauthorizedPendingDepositAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"}],"name":"VidOutOfRange","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"}],"name":"WithdrawZeroBalance","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"boostid","type":"uint256"}],"name":"AddBoost","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"}],"name":"AddVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"_boostID","type":"uint256"}],"name":"BoostEmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wantLockedTotal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalSupply","type":"uint256"}],"name":"Earned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"boostid","type":"uint256"}],"name":"EnableBoost","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"FailedEarn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"reason","type":"bytes"}],"name":"FailedEarnBytes","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"FailedWithdrawFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"reason","type":"bytes"}],"name":"FailedWithdrawFeeBytes","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"targetShares","type":"uint256"}],"name":"MaximizerHarvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"},{"indexed":false,"internalType":"bool","name":"earnBeforeDeposit","type":"bool"},{"indexed":false,"internalType":"bool","name":"earnBeforeWithdraw","type":"bool"}],"name":"SetAutoEarn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"vid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"vid","type":"uint256"}],"name":"boostInfo","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"contract IBoostPool","name":"pool","type":"address"},{"internalType":"contract IERC20","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"pendingReward","type":"uint256"}],"internalType":"struct IVaultHealer.BoostInfo[]","name":"active","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"contract IBoostPool","name":"pool","type":"address"},{"internalType":"contract IERC20","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"pendingReward","type":"uint256"}],"internalType":"struct IVaultHealer.BoostInfo[]","name":"finished","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"contract IBoostPool","name":"pool","type":"address"},{"internalType":"contract IERC20","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"pendingReward","type":"uint256"}],"internalType":"struct IVaultHealer.BoostInfo[]","name":"available","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_boostID","type":"uint256"}],"name":"boostPool","outputs":[{"internalType":"contract IBoostPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"},{"internalType":"uint16","name":"n","type":"uint16"}],"name":"boostPoolVid","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"contract IBoostPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"},{"internalType":"address","name":"_implementation","type":"address"},{"internalType":"bytes","name":"initdata","type":"bytes"}],"name":"createBoost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"targetVid","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"createMaximizer","outputs":[{"internalType":"uint256","name":"vid","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IStrategy","name":"_implementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"createVault","outputs":[{"internalType":"uint16","name":"vid","type":"uint16"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vid","type":"uint256"},{"internalType":"uint256","name":"_wantAmt","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"vids","type":"uint256[]"}],"name":"earn","outputs":[{"internalType":"uint256[]","name":"successGas","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"vids","type":"uint256[]"},{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"earn","outputs":[{"internalType":"uint256[]","name":"successGas","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_boostID","type":"uint256"}],"name":"emergencyBoostWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_boostID","type":"uint256"}],"name":"enableBoost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_boostID","type":"uint256"}],"name":"enableBoost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint192","name":"_amount","type":"uint192"}],"name":"executePendingDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_boostID","type":"uint256"}],"name":"harvestBoost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vid","type":"uint256"}],"name":"harvestMaximizer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vid","type":"uint256"}],"name":"harvestTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vid","type":"uint256"},{"internalType":"uint256","name":"_wantAmt","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"maximizerDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"maximizerEarningsOffset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_vid","type":"uint256"}],"name":"maximizerPendingTargetShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"}],"name":"nextBoostPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"contract IBoostPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numVaultsBase","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"},{"internalType":"bool","name":"panic","type":"bool"}],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"}],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"vids","type":"uint256[]"}],"name":"paused","outputs":[{"internalType":"bytes","name":"pausedArray","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"rescue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"},{"internalType":"bool","name":"earnBeforeDeposit","type":"bool"},{"internalType":"bool","name":"earnBeforeWithdraw","type":"bool"}],"name":"setAutoEarn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_uri","type":"string"}],"name":"setURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"vid","type":"uint256"}],"name":"stakedWantTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vid","type":"uint256"}],"name":"strat","outputs":[{"internalType":"contract IStrategy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256[]","name":"vids","type":"uint256[]"}],"name":"tokenData","outputs":[{"internalType":"uint256[4][]","name":"data","type":"uint256[4][]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_vid","type":"uint256"}],"name":"totalBalanceOf","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_account","type":"address[]"},{"internalType":"uint256[]","name":"_vid","type":"uint256[]"}],"name":"totalBalanceOfBatch","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalMaximizerEarnings","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"}],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultFeeManager","outputs":[{"internalType":"contract IVaultFeeManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vaultInfo","outputs":[{"internalType":"contract IERC20","name":"want","type":"address"},{"internalType":"uint8","name":"noAutoEarn","type":"uint8"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint48","name":"lastEarnBlock","type":"uint48"},{"internalType":"uint16","name":"numBoosts","type":"uint16"},{"internalType":"uint16","name":"numMaximizers","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vhAuth","outputs":[{"internalType":"contract VaultHealerAuth","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"}],"name":"wantLockedTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vid","type":"uint256"},{"internalType":"uint256","name":"_wantAmt","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vid","type":"uint256"},{"internalType":"uint256","name":"_wantAmt","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zap","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60e060405234620000d3576200611e6060813803918262000020816200011e565b938492833981010312620000d357806200003e620000629262000144565b906200005b6040620000536020840162000144565b920162000144565b9162000159565b604051615dfd908162000321823960805181818161092c015281816111eb01528181611332015281816128d2015281816140cc015281816149400152818161502f015281816151b5015261587f015260a051818181610dd501526133e0015260c051818181610a36015261319a0152f35b600080fd5b50634e487b7160e01b600052604160045260246000fd5b60405190602082016001600160401b038111838210176200010f57604052565b62000119620000d8565b604052565b6040519190601f01601f191682016001600160401b038111838210176200010f57604052565b51906001600160a01b0382168203620000d357565b62000163620000ef565b60008082526001908190556001600160a01b0392831660a052929091166080528051906001600160401b0382116200027a575b620001ae82620001a86006546200028a565b620002c7565b60209081601f8411600114620001f15750928293918392600094620001e5575b50501b916000199060031b1c19161760065560c052565b015192503880620001ce565b6006600052919083601f1981167ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f946000905b888383106200025f575050501062000245575b505050811b0160065560c052565b015160001960f88460031b161c1916905538808062000237565b85870151885590960195948501948793509081019062000224565b62000284620000d8565b62000196565b90600182811c92168015620002bc575b6020831014620002a657565b634e487b7160e01b600052602260045260246000fd5b91607f16916200029a565b601f8111620002d4575050565b6000906006825260208220906020601f850160051c8301941062000315575b601f0160051c01915b8281106200030957505050565b818155600101620002fc565b9092508290620002f356fe60806040526004361015610022575b3461001d5761001b6135f3565b005b600080fd5b60003560e01c8062dde10e1461041c578062fdd58e1461041357806301ffc9a71461040a57806302fe53051461040157806304560ce9146103f85780630a4b60d6146103ef5780630e89341c146103e65780631b0f97bf146103dd578063262d6152146103d45780632eb2c2d6146103cb5780632f914bd4146103c257806342722229146103b9578063456d067e146103b05780634e1273f4146103a7578063525918c41461039e57806355d7213d146103955780635f53f4c51461038c57806366afea4114610383578063744fb6ca1461037a5780637ba0e96d14610371578063839006f21461036857806383d3abe91461035f57806384a4bd841461035657806386abfe931461034d578063878003b81461034457806389fb6a7a1461033b57806391bb29c814610332578063929060d81461032957806393578b0414610320578063991f7c80146103175780639b1b2c811461030e5780639bec76ab146103055780639ed067de146102fc578063a2028bee146102f3578063a22cb465146102ea578063a31d40ac146102e1578063aa0b7db7146102d8578063ac9650d8146102cf578063aff8631c146102c6578063b997cb28146102bd578063bd85b039146102b4578063c12e1ad9146102ab578063ca1a2939146102a2578063d1741f6814610299578063e8da52b014610290578063e985e9c514610287578063ea6dfa4b1461027e578063ed34a04b14610275578063f0c0a2031461026c578063f242432a146102635763fabc1cbc0361000e5761025e612cf1565b61000e565b5061025e612c3e565b5061025e612b37565b5061025e612a25565b5061025e61292b565b5061025e6128f6565b5061025e6128b1565b5061025e612785565b5061025e6126f1565b5061025e6125bd565b5061025e612590565b5061025e6123e0565b5061025e612393565b5061025e6122f2565b5061025e6120e5565b5061025e61205a565b5061025e611fa3565b5061025e611ee3565b5061025e611eb3565b5061025e611d9b565b5061025e611c9a565b5061025e611a9a565b5061025e611950565b5061025e6118d9565b5061025e61182b565b5061025e61177c565b5061025e6116d9565b5061025e6114e9565b5061025e6114bd565b5061025e61149a565b5061025e6112e2565b5061025e611193565b5061025e611115565b5061025e611044565b5061025e610fec565b5061025e610eff565b5061025e610ece565b5061025e610e51565b5061025e610db4565b5061025e610ce7565b5061025e610cbe565b5061025e610c38565b5061025e610a15565b5061025e6108de565b5061025e610766565b5061025e6106d4565b5061025e610681565b5061025e610550565b5061025e61049c565b5061025e610456565b503461001d57602036600319011261001d57602061043b6004356135b7565b6040519015158152f35b6001600160a01b0381160361001d57565b503461001d57604036600319011261001d57602061048260043561047981610445565b60243590613058565b604051908152f35b6001600160e01b031981160361001d57565b503461001d57602036600319011261001d5760206004356104bc8161048a565b63ffffffff60e01b16636cdb3d1360e11b8114908115610512575b8115610501575b81156104f0575b506040519015158152f35b6316c6527960e11b149050386104e5565b6301ffc9a760e01b811491506104de565b6303a24d0760e21b811491506104d7565b9181601f8401121561001d578235916001600160401b03831161001d576020838186019501011161001d57565b503461001d5760208060031936011261001d576001600160401b0360043581811161001d57610586610595913690600401610523565b61058e61339a565b3691610be6565b918251918211610674575b6105b4826105af60065461301e565b6131c0565b80601f83116001146105ed575081926000926105e2575b50508160011b916000199060031b1c191617600655005b0151905038806105cb565b90601f1983169361062060066000527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90565b926000905b86821061065c5750508360019510610643575b505050811b01600655005b015160001960f88460031b161c19169055388080610638565b80600185968294968601518155019501930190610625565b61067c610a5a565b6105a0565b503461001d57602036600319011261001d576106b560043580600052600260205261ffff60406000205460e01c169061366b565b604080519283526001600160a01b0391909116602083015290f35b0390f35b503461001d57602036600319011261001d5760043560005260096020526020604060002054604051908152f35b91908251928382526000905b84821061073a57509280602093941161072d575b601f01601f1916010190565b6000838284010152610721565b9060209081808285010151908286010152019061070d565b906020610763928181520190610701565b90565b503461001d5760208060031936011261001d5760405160009160065461078b8161301e565b8084529060019081811690811561081c57506001146107c1575b6106d0846107b581880382610afd565b60405191829182610752565b600660009081529294507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f5b82841061080957505050816106d0936107b592820101936107a5565b80548585018701529285019281016107ed565b925050506106d0935060ff1916908201526107b58160408101936107a5565b9181601f8401121561001d578235916001600160401b03831161001d576020808501948460051b01011161001d57565b602060031982011261001d57600435906001600160401b03821161001d576108959160040161083b565b9091565b90815180825260208080930193019160005b8281106108b9575050505090565b8351855293810193928101926001016108ab565b906020610763928181520190610899565b503461001d576108ed3661086b565b6000916108fe600284541415613487565b60028355604051632c34936b60e21b81528381806109208686600484016138a5565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156109fd575b84916109db575b50610967836130a6565b935b83811061098a576106d08561097e6001600055565b604051918291826108cd565b6109ba905a6109af61099d8388886132cb565b356109a88487612eb7565b5190613a84565b6109bf575b50612e35565b610969565b6109ca905a906138d2565b6109d48288612eb7565b52386109b4565b6109f791503d8086833e6109ef8183610afd565b81019061381e565b3861095d565b610a056132f2565b610956565b600091031261001d57565b503461001d57600036600319011261001d5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50634e487b7160e01b600052604160045260246000fd5b6001600160401b038111610a8457604052565b610a8c610a5a565b604052565b608081019081106001600160401b03821117610a8457604052565b602081019081106001600160401b03821117610a8457604052565b606081019081106001600160401b03821117610a8457604052565b604081019081106001600160401b03821117610a8457604052565b90601f801991011681019081106001600160401b03821117610a8457604052565b60405190610b2b82610a91565b565b6020906001600160401b038111610b46575b60051b0190565b610b4e610a5a565b610b3f565b9291610b5e82610b2d565b91610b6c6040519384610afd565b829481845260208094019160051b810192831161001d57905b828210610b925750505050565b81358152908301908301610b85565b9080601f8301121561001d5781602061076393359101610b53565b6020906001600160401b038111610bd9575b601f01601f19160190565b610be1610a5a565b610bce565b929192610bf282610bbc565b91610c006040519384610afd565b82948184528183011161001d578281602093846000960137010152565b9080601f8301121561001d5781602061076393359101610be6565b503461001d5760a036600319011261001d57600435610c5681610445565b60243590610c6382610445565b6001600160401b039160443583811161001d57610c84903690600401610ba1565b60643584811161001d57610c9c903690600401610ba1565b9160843594851161001d57610cb861001b953690600401610c1d565b93615988565b503461001d57602036600319011261001d5760206104826004356132ff565b8015150361001d57565b503461001d57606036600319011261001d576004357f7580964b10922c3a325aa7ff4f93acdf540a02246eda09575b660dd1d53bfc9b602435610d2981610cdd565b60443590610d3682610cdd565b610d3e61339a565b610d4784613524565b8015610dad5760005b8215610da65760005b85600052600260205260406000209182549160ff60a01b911760a01b169060ff60a01b1916179055610da1604051928392839092916020906040830194151583521515910152565b0390a2005b6002610d59565b6001610d50565b503461001d57600036600319011261001d5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b9291610e0482610b2d565b91610e126040519384610afd565b829481845260208094019160051b810192831161001d57905b828210610e385750505050565b8380918335610e4681610445565b815201910190610e2b565b503461001d57604036600319011261001d576001600160401b0360043581811161001d573660238201121561001d57610e94903690602481600401359101610df9565b9060243590811161001d576106d091610eb4610eba923690600401610ba1565b906130d8565b604051918291602083526020830190610899565b503461001d57602036600319011261001d5760206001600160a01b03610ef630600435615d24565b16604051908152f35b503461001d57606036600319011261001d57602435600435610f2082610445565b6044356001600160401b03811161001d57610f3f903690600401610523565b92610f4983613524565b610f5161339a565b73d40fdcd3569859ff09ea9f8070a41bd8c9b6487690813b1561001d57600093610fbb6001600160a01b0394604051978896879586956326b40dc560e11b875260026004880152600b6024880152604487015216606485015260a0608485015260a48401916134d3565b03915af48015610fdf575b610fcc57005b80610fd961001b92610a71565b80610a0a565b610fe76132f2565b610fc6565b503461001d57602036600319011261001d5760043561103b600091611015600284541415613487565b600283556110238133613058565b90808452600860205281604085205492839233615b77565b60018155604051f35b503461001d57602036600319011261001d576000611066600282541415613487565b60028155806001600160a01b0361107f30600435615d24565b16803b156110e057819060246040518094819363072e008f60e11b83523360048401525af180156110d3575b6110c0575b506110bb6001600055565b604051f35b80610fd96110cd92610a71565b386110b0565b6110db6132f2565b6110ab565b50fd5b606060031982011261001d576004359160243591604435906001600160401b03821161001d5761089591600401610523565b503461001d57611145611127366110e3565b92909161113960026000541415613487565b60026000553391614ee0565b506001600055005b604060031982011261001d576001600160401b039160043583811161001d57826111799160040161083b565b9390939260243591821161001d576108959160040161083b565b503461001d576111a23661114d565b92906000936111b5600286541415613487565b600285558381036112bd57604051632c34936b60e21b8152908582806111df8888600484016138a5565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9182156112b0575b8692611294575b50611226856130a6565b955b85811061123d576106d08761097e6001600055565b61127a905a611270611250838a8a6132cb565b3561125b8488612eb7565b519061126885888b612e68565b929091613d29565b61127f5750612e35565b611228565b61128a905a906138d2565b6109d4828a612eb7565b6112a99192503d8088833e6109ef8183610afd565b903861121c565b6112b86132f2565b611215565b6040516302a7fbbb60e21b8152600481018590526024810191909152604490fd5b0390fd5b503461001d5760208060031936011261001d576000906004359061130582610445565b604080516314e11f2560e11b81526000600482015290926113ce9183916001600160a01b039086836024817f000000000000000000000000000000000000000000000000000000000000000086165afa92831561148d575b889361145d575b5081831615611455575b86516370a0823160e01b8152306004820152929116908383602481855afa928315611448575b8893611419575b5087875180968195829463a9059cbb60e01b845260048401602090939291936001600160a01b0360408201951681520152565b03925af1801561140c575b6113e257505051f35b8161140192903d10611405575b6113f98183610afd565b810190613385565b5051f35b503d6113ef565b6114146132f2565b6113d9565b61143a919350843d8611611441575b6114328183610afd565b8101906132e3565b913861139b565b503d611428565b6114506132f2565b611394565b33925061136e565b61147e919350873d8911611486575b6114768183610afd565b810190613360565b509138611364565b503d61146c565b6114956132f2565b61135d565b503461001d57600036600319011261001d57602061ffff60015416604051908152f35b503461001d57604036600319011261001d5760206104826004356114e081610445565b60243590615c9d565b503461001d57602036600319011261001d5760043560009061150f600283541415613487565b6002825561153d6115398260ff6001918060081c600052600b602052161b60406000205416151590565b1590565b6116bf5761158081611562336001600160a01b0316600052600c602052604060002090565b60019160ff918160081c600052602052161b60406000205416151590565b61169f576115c2816115a5336001600160a01b0316600052600c602052604060002090565b8160081c600052602052600160ff604060002092161b8154179055565b816115df6115d36115d33085615d24565b6001600160a01b031690565b6116016115f56001600160e01b03851633613058565b6001600160701b031690565b813b1561169b5760405163115b37af60e31b81523360048201526001600160701b0391909116602482015291908290604490829084905af1801561168e575b61167b575b5060405190337f2f1ae043953d412f314a1c3ab459e4b5adadb5b05b7789242df81c5e5d6fe6828484a36116796001600055565bf35b80610fd961168892610a71565b38611645565b6116966132f2565b611640565b8280fd5b604051636e7d521d60e11b81523360048201526024810191909152604490fd5b60405163b8cdefd360e01b81526004810191909152602490fd5b503461001d57604036600319011261001d576004356116f781610445565b6024356117048183613058565b918160101b916000526002602052600161172660406000205460f01c846135ab565b928119811161176f575b019291905b8184111561174857604051838152602090f35b90916117616117679161175b8685615c9d565b906135ab565b93612e35565b929190611735565b611777612e1e565b611730565b503461001d57602036600319011261001d576004356117a060026000541415613487565b60026000558060101b90600052600260205260016117c660406000205460f01c836135ab565b918119811161181e575b015b818111156117e45761001b6001600055565b806118146117f56118199333613058565b611809836000526008602052604060002090565b548181928533615b77565b612e35565b6117d2565b611826612e1e565b6117d0565b503461001d5761183a3661086b565b9061184482610bbc565b916118526040519384610afd565b808352601f1961186182610bbc565b0190602091368386013760005b81811061188357604051806106d08782610752565b8061189b6118956118c29385886132cb565b356135b7565b600090156118d45750600160f81b5b86518210156118c7575b60001a848288010153612e35565b61186e565b6118cf612e51565b6118b4565b6118aa565b503461001d57602036600319011261001d57600435600052600260205260c0604060002054604051906001600160a01b038116825260ff8160a01c16602083015260ff8160a81c161515604083015265ffffffffffff8160b01c16606083015261ffff8160e01c16608083015260f01c60a0820152f35b503461001d57604036600319011261001d5760043560243561197181610cdd565b61197961339a565b61198282613524565b6119a3611999836000526002602052604060002090565b5460a81c60ff1690565b6119a957005b6119fb575b6119d26119c5826000526002602052604060002090565b805460ff60a81b19169055565b7f32fb7c9891bc4f963c7de9f1186d2a7755c7d6e9f4604dabe1d8bb3027c2f49e6000604051a2005b611a0f816000526003602052604060002090565b54428111611a805750611a2142613589565b50611a2e6115d382615d95565b803b1561001d5760008091600460405180948193634700d30560e01b83525af18015611a73575b611a60575b506119ae565b80610fd9611a6d92610a71565b38611a5a565b611a7b6132f2565b611a55565b60405163b2ac163b60e01b81526004810191909152602490fd5b503461001d5760408060031936011261001d57600435611ab981610445565b602435600092611acd600285541415613487565b600284556001600160a01b038316928333141580611c87575b611c6057611b106115398460ff6001918060081c600052600b602052161b60406000205416151590565b611c4857611b3583611562836001600160a01b0316600052600c602052604060002090565b611c1f57611b5a836115a5836001600160a01b0316600052600c602052604060002090565b84611b6b6115d36115d33087615d24565b611b816115f56001600160e01b03871685613058565b90803b1561169b57845163115b37af60e31b81526001600160a01b0390941660048501526001600160701b03909116602484015282908183816044810103925af18015611c12575b611bff575b5051917f2f1ae043953d412f314a1c3ab459e4b5adadb5b05b7789242df81c5e5d6fe6828484a36116796001600055565b80610fd9611c0c92610a71565b38611bce565b611c1a6132f2565b611bc9565b9051636e7d521d60e11b81526001600160a01b0390911660048201526024810191909152604490fd5b815163b8cdefd360e01b815260048101849052602490fd5b9051635a1c589760e11b81526001600160a01b039091166004820152336024820152604490fd5b50611c95611539338361314f565b611ae6565b503461001d57604036600319011261001d576024356004356001600160401b03821161001d576020611cd36106d0933690600401610523565b9092611cde81613524565b611ce661339a565b611cf560026000541415613487565b6002600055611d2c6040519485938493630bd5ac8d60e21b85526002600486015260248501526060604485015260648401916134d3565b038173d40fdcd3569859ff09ea9f8070a41bd8c9b648765af4908115611d8e575b600091611d70575b50611d606001600055565b6040519081529081906020820190565b611d88915060203d8111611441576114328183610afd565b38611d55565b611d966132f2565b611d4d565b503461001d57604036600319011261001d57600435611db981610445565b6024356001600160c01b0381169081900361001d57336000908152600a60205260409020546001600160a01b0381169290611e039060a01c5b6bffffffffffffffffffffffff1690565b611e336001611e25336001600160a01b0316600052600a602052604060002090565b01546001600160a01b031690565b90611e61611df26001611e59336001600160a01b0316600052600a602052604060002090565b015460a01c90565b9060601b178311611ea15761001b93611e9c611e90336001600160a01b0316600052600a602052604060002090565b60016000918281550155565b61523f565b6040516368228c0b60e11b8152600490fd5b503461001d57602036600319011261001d576020611ed2600435615d95565b6001600160a01b0360405191168152f35b50611eed366110e3565b91611ef7846135b7565b611f8a576001600160a01b03611f0c85615d95565b163303611f4557611f4192611f3992611f28928660101c613e9e565b926000526009602052604060002090565b9182546135ab565b9055005b60405162461bcd60e51b815260206004820152601d60248201527f56483a2073656e64657220646f6573206e6f74206d61746368207669640000006044820152606490fd5b6040516388778eb360e01b815260048101859052602490fd5b503461001d57604036600319011261001d57600435611fc181610445565b602435611fcd81610cdd565b6001600160a01b0382169182331461204857612009903360005260056020526040600020906001600160a01b0316600052602052604060002090565b9015159060ff1981541660ff83161790556040519081527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b604051634aa2aa8560e11b8152600490fd5b503461001d57604036600319011261001d5760043561207881610445565b60243590816001600160a01b0382166120d65750506000905b600090826120a85750505060206000604051908152f35b60406120d1926120c26020956120bd856132ff565b613231565b92815260088552205490613250565b610482565b6120df91613058565b90612091565b506120ef366110e3565b916120f9846135b7565b611f8a5761210c60026000541415613487565b6002600055612135612128856000526002602052604060002090565b546001600160a01b031690565b6040516370a0823160e01b815233600482015260209591906001600160a01b038216908781602481855afa908115612283575b600091612266575b508481106122305750604051636eb1769f60e11b8152336004820152306024820152908790829060449082905afa968715612223575b600097612204575b50508286106121cd57506121c29450613e9e565b5061001b6001600055565b604051630ff2bf3560e31b81526001600160a01b039190911660048201523360248201526044810186905260648101839052608490fd5b61221b929750803d10611441576114328183610afd565b9438806121ae565b61222b6132f2565b6121a6565b60405163861da4bd60e01b81526001600160a01b0384166004820152336024820152604481019190915260648101859052608490fd5b61227d9150883d8a11611441576114328183610afd565b38612170565b61228b6132f2565b612168565b602080820190808352835180925260408301928160408460051b8301019501936000915b8483106122c45750505050505090565b90919293949584806122e2600193603f198682030187528a51610701565b98019301930191949392906122b4565b503461001d576123013661086b565b9061230b82610b2d565b916123196040519384610afd565b808352601f1961232882610b2d565b0160005b81811061238257505060005b81811061234d57604051806106d08682612290565b8061236861236261058e61237d948688612e68565b30612eec565b6123728287612eb7565b526109b48186612eb7565b612338565b80606060208093880101520161232c565b503461001d57604036600319011261001d576001600160a01b036004356123b981610445565b16600052600760205260406000206024356000526020526020604060002054604051908152f35b503461001d57602036600319011261001d5760043561240460026000541415613487565b600260005561242d61153982611562336001600160a01b0316600052600c602052604060002090565b612570576125167ff250381d9cf54c6243fb79f1dd2f73730df468eda9cd44a2f38f3ef88a4e41cb916124666115d36115d33084615d24565b604051631bfc726f60e21b8152336004820152906020908290602490829060009062080000f160009181612550575b5061252357506124bf818060081c600052600b602052600160ff604060002092161b198154169055565b6124fe816124e0336001600160a01b0316600052600c602052604060002090565b8160081c600052602052600160ff604060002092161b198154169055565b60408051338152602081019290925290918291820190565b0390a161001b6001600055565b6124bf5761254b818060081c600052600b602052600160ff604060002092161b198154169055565b6124bf565b61256991925060203d8111611405576113f98183610afd565b9038612495565b604051636096427160e11b81523360048201526024810191909152604490fd5b503461001d57602036600319011261001d5760043560005260086020526020604060002054604051908152f35b503461001d57604036600319011261001d576004356125db81610445565b6024356001600160401b03811161001d576125fa903690600401610523565b919061260461339a565b61261360026000541415613487565b6002600055600161ffff81541661fffe81116126d8575b01916126428361ffff1661ffff196001541617600155565b73d40fdcd3569859ff09ea9f8070a41bd8c9b6487693843b1561001d576106d094612688936000936040519586948593849363010e826f60e21b85528a600486016134f4565b03915af480156126cb575b6126b8575b506126a36001600055565b60405161ffff90911681529081906020820190565b80610fd96126c592610a71565b38612698565b6126d36132f2565b612693565b6126e0612e1e565b61262a565b61ffff81160361001d57565b503461001d57604036600319011261001d576106b5602435612712816126e5565b60043561366b565b602090816040818301928281528551809452019301916000805b83821061274357505050505090565b9091929394838651829084915b6004831061276f57505050608060019201960192019093929193612734565b8151815287936001909301929182019101612750565b503461001d57604036600319011261001d576004356127a381610445565b6024356001600160401b03811161001d576127c290369060040161083b565b6127cb81613270565b926001600160a01b038116159160005b8181106127f057604051806106d0888261271a565b808661280061289a9385896132cb565b35612815816000526008602052604060002090565b549081156128a9578161283261282b8686612eb7565b5160600190565b5261283c816132ff565b84898261285361284c8489612eb7565b5160400190565b5261289f576128899461287e61287761286f612884968d613058565b938493612eb7565b5160200190565b52613231565b613250565b6128938289612eb7565b5152612e35565b6127db565b5050505050612e35565b505050612e35565b503461001d57600036600319011261001d5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461001d57604036600319011261001d57602061043b60043561291981610445565b6024359061292682610445565b61314f565b503461001d57608036600319011261001d5760443561294981610445565b606435906001600160401b03821161001d5761296c611145923690600401610523565b9161297c60026000541415613487565b6002600055602435600435614ee0565b90815180825260208080930193019160005b8281106129ac575050505090565b835180518652808301516001600160a01b039081168785015260408083015190911690870152606090810151908601526080909401939281019260010161299e565b91612a1790612a09610763959360608652606086019061298c565b90848203602086015261298c565b91604081840391015261298c565b503461001d57604036600319011261001d57600435612a4381610445565b6000602435808252600260205261ffff604083205460e01c16612ac4612a7c856001600160a01b0316600052600c602052604060002090565b60405163e6f0fd7560e01b815261ffff9093166004840152600b602484015260448301526001600160a01b0390941660648201526084810191909152918290819060a4820190565b038173d40fdcd3569859ff09ea9f8070a41bd8c9b648765af4908115612b2a575b600091612b09575b5080516106d060406020840151930151604051938493846129ee565b612b24913d8091833e612b1c8183610afd565b8101906136b7565b38612aed565b612b326132f2565b612ae5565b503461001d57612b463661114d565b929091612b62612b57368385610df9565b610eb4368787610b53565b9160005b8351811015612c3057612bba612b87612b808389896132cb565b3560101b90565b61175b612bb3612bac612b9b868c8c6132cb565b356000526002602052604060002090565b5460f01c90565b61ffff1690565b612bd0612bcb612b80848a8a6132cb565b61359b565b81811115612be8575050612be390612e35565b612b66565b80612c1b612c0b612c2b93612c06612c01888b8b6132cb565b615d1a565b615c9d565b612c15868a612eb7565b516135ab565b612c258589612eb7565b52612e35565b612bd0565b604051806106d086826108cd565b503461001d5760a036600319011261001d57600435612c5c81610445565b602435612c6881610445565b6084356001600160401b03811161001d57612c87903690600401610c1d565b90612c9760026000541415613487565b6002600055336001600160a01b038416148015612ce1575b15612ccf57612cc59260643591604435916155f5565b61001b6001600055565b60405163035515e360e01b8152600490fd5b50612cec338461314f565b612caf565b503461001d57602036600319011261001d57600435612d0e61339a565b612d1781613524565b8060101c80151580612e0f575b612df55750612d43611539611999836000526002602052604060002090565b612d4957005b612d73612d60826000526002602052604060002090565b805460ff60a81b1916600160a81b179055565b612d7f6115d382615d95565b803b1561001d57600080916004604051809481936365a5e30b60e01b83525af18015612de8575b612dd5575b507f9dd715fed52c25e642f97653bb4c4339ad98fe6d6e130348be82ae5d86383a8c6000604051a2005b80610fd9612de292610a71565b38612dab565b612df06132f2565b612da6565b6040516388778eb360e01b81526004810191909152602490fd5b50612e19816135b7565b612d24565b50634e487b7160e01b600052601160045260246000fd5b6001906000198114612e45570190565b612e4d612e1e565b0190565b50634e487b7160e01b600052603260045260246000fd5b9190811015612eaa575b60051b81013590601e198136030182121561001d5701908135916001600160401b03831161001d57602001823603811361001d579190565b612eb2612e51565b612e72565b6020918151811015612ecc575b60051b010190565b612ed4612e51565b612ec4565b60405190612ee682610aac565b60008252565b9060405190612efa82610ac7565b602782527f416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c6020830152660819985a5b195960ca1b6040830152823b15612f5a576000816107639460208394519201905af4612f54612fae565b90612fde565b60405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608490fd5b3d15612fd9573d90612fbf82610bbc565b91612fcd6040519384610afd565b82523d6000602084013e565b606090565b90919015612fea575090565b815115612ffa5750805190602001fd5b60405162461bcd60e51b8152602060048201529081906112de906024830190610701565b90600182811c9216801561304e575b602083101461303857565b634e487b7160e01b600052602260045260246000fd5b91607f169161302d565b6001600160a01b03811615613094576130909160005260046020526040600020906001600160a01b0316600052602052604060002090565b5490565b604051631f4212a760e01b8152600490fd5b906130b082610b2d565b6130bd6040519182610afd565b82815280926130ce601f1991610b2d565b0190602036910137565b919091805183510361313d576130ee81516130a6565b9060005b815181101561313657806131276001600160a01b036131146131319486612eb7565b51166131208389612eb7565b5190613058565b612c258286612eb7565b6130f2565b5090925050565b604051637edbce1b60e01b8152600490fd5b906001600160a01b03809216600052600560205260ff613186826040600020906001600160a01b0316600052602052604060002090565b541691821561319457505090565b809192507f00000000000000000000000000000000000000000000000000000000000000001691161490565b601f81116131cc575050565b600090600682527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f906020601f850160051c83019410613227575b601f0160051c01915b82811061321c57505050565b818155600101613210565b9092508290613207565b8060001904821181151516613244570290565b61324c612e1e565b0290565b811561325a570490565b634e487b7160e01b600052601260045260246000fd5b9061327a82610b2d565b60409061328982519182610afd565b838152809361329a601f1991610b2d565b019160005b8381106132ac5750505050565b60209082516132ba81610a91565b60803682378282860101520161329f565b91908110156132db5760051b0190565b610b4e612e51565b9081602091031261001d575190565b506040513d6000823e3d90fd5b60206001600160a01b03613314600493615d95565b16604051928380926342da4eb360e01b82525afa908115613353575b60009161333b575090565b610763915060203d8111611441576114328183610afd565b61335b6132f2565b613330565b919082604091031261001d576020825161337981610445565b920151610763816126e5565b9081602091031261001d575161076381610cdd565b6000356001600160e01b03198181169160043610613472575b5050604051632474521560e21b81526001600160e01b0319821660048201523360248201526020816044817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115613465575b600091613447575b50156134235750565b604051632db4363760e11b81526001600160e01b0319919091166004820152602490fd5b61345f915060203d8111611405576113f98183610afd565b3861341a565b61346d6132f2565b613412565b908092503660040360031b1b161638806133b3565b1561348e57565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b908060209392818452848401376000828201840152601f01601f1916010190565b6001600160a01b03610763959361ffff6080946002855216602084015216604082015281606082015201916134d3565b61ffff8082168015918215613555575b505061353d5750565b6024906040519063afcbb58f60e01b82526004820152fd5b9091506000838303613571575080600154165b16103880613534565b6040908460101c81526002602052205460f01c613568565b61546090615460198111612e45570190565b6001906001198111612e45570190565b81198111612e45570190565b80600052600260205260ff60406000205460a81c16159081156135d8575090565b60101c8015159150816135e9575090565b61076391506135b7565b50333b1561360e575b604051635a93724d60e01b8152600490fd5b7f577cbdbf32026552c0ae211272febcff3ea352b0c755f8f39b49856dcac71019548061363b57506135fc565b69363d3d373d3d3d363d7360b01b600052600a526e5af43d82803e903d91602b57fd5bf360881b601e52602d6000f35b9061ffff1663b0057000908063ffffffff0382116136aa575b0160e01b6001600160e01b03191617906001600160a01b036136a63084615d24565b1690565b6136b2612e1e565b613684565b602090818184031261001d5780516001600160401b039182821161001d5701601f9380858301121561001d576040918251956136f287610ac7565b86936060928381019685881161001d5781965b888810613719575050505050505050505090565b875182811161001d57830187868201121561001d5780519061373a82610b2d565b9161374787519384610afd565b8083528c8084019160071b830101918a831161001d57918d8094929593015b81811061377d575050829350815201970196613705565b91935091936080828c031261001d578d608091895161379b81610a91565b84518152828501516137ac81610445565b838201528a8501516137bd81610445565b8b8201528c8501518d8201528152019101918d93919492613766565b9080601f8301121561001d57604051916137f283610ac7565b82906060810192831161001d57905b82821061380e5750505090565b8151815260209182019101613801565b602090818184031261001d578051906001600160401b03821161001d570182601f8201121561001d5780519161385383610b2d565b936138616040519586610afd565b838552818501908260608096028501019381851161001d578301915b84831061388d5750505050505090565b83869161389a84866137d9565b81520192019161387d565b602080825281018390526001600160fb1b03831161001d5760409260051b80928483013701016000815290565b8181106138dd570390565b6138e5612e1e565b0390565b919082604091031261001d576020825161390281610cdd565b92015190565b9190600090835b6003831061394257505050906001600160a01b0360c09216606082015260a06080820152600060a0820152016000815290565b60019082518152602080910192019201919061390f565b9190600090835b6003831061399357505050906001600160a01b0360c09216606082015260a06080820152600060a0820152016000815290565b600190825181526020809101920192019190613960565b94939190600090865b600383106139e15750505061076393946001600160a01b0360a09216606082015281608082015201916134d3565b6001908251815260208091019201920191906139b3565b60009060033d11613a0557565b905060046000803e60005160e01c90565b600060443d1061076357604051600319913d83016004833e81516001600160401b03918282113d602484011117613a7357818401948551938411613a7b573d85010160208487010111613a73575061076392910160200190610afd565b949350505050565b50949350505050565b90613a99826000526002602052604060002090565b90613aa3836135b7565b8015613c6f575b613c6757815465ffffffffffff60b01b19164360b01b65ffffffffffff60b01b1617909155600090613ade6115d384615d95565b90613b046040928392835195868094819363b1c6e63b60e01b8352339060048401613908565b03925af191826000918294613c36575b50613bd1575090506001613b266139f8565b6308c379a014613b7f575b613b3d575b5050600090565b613b757f3e576e666560ade9dad25b8a67d1dbdb9bd42cc0e6e6996f7d67170e29f3ff1e91613b6a612fae565b905191829182610752565b0390a23880613b36565b613b87613a16565b80613b93575b50613b31565b9050827fe6142c1d04a23dd96ddced0a9a1d6cadd1d22a86d75f86aa435e3a66e658d27e613bc8600093855191829182610752565b0390a238613b8d565b613bdd57505050600090565b7f01cfd617a8534d57de2a17bb930e4bfcc4a51c198f6608a4ba5eb99f2799a27791613c2e613c16856000526008602052604060002090565b54925191825260208201929092529081906040820190565b0390a2600190565b909350613c599150823d8411613c60575b613c518183610afd565b8101906138e9565b9238613b14565b503d613c47565b505050600090565b50815460b01c65ffffffffffff1665ffffffffffff164314613aaa565b90613ca1826000526002602052604060002090565b90613cab836135b7565b8015613d0c575b613c6757815465ffffffffffff60b01b19164360b01b65ffffffffffff60b01b1617909155600090613ce66115d384615d95565b90613b046040928392835195868094819363b1c6e63b60e01b8352339060048401613959565b50815460b01c65ffffffffffff1665ffffffffffff164314613cb2565b9290613d3f846000526002602052604060002090565b91613d49856135b7565b8015613db7575b613dad57825465ffffffffffff60b01b19164360b01b65ffffffffffff60b01b1617909255613b049260009290613d896115d387615d95565b90604094859485519788958694859363b1c6e63b60e01b85523390600486016139aa565b5050505050600090565b50825460b01c65ffffffffffff1665ffffffffffff164314613d50565b9060608282031261001d57610763916137d9565b815160208301516001600160a01b0391821673ffffffffffffffffffffffffffffffffffffffff1960a092831b8116919091178455604085015160609095015190911b16921691909117600190910155565b909161076395936080936001600160a01b039283809216855216602084015216604082015281606082015201916134d3565b919082604091031261001d576020825192015190565b6107639392606092825260208201528160408201520190610701565b9190613f4490949394613ebb846000526008602052604060002090565b548015158061414e575b6140a8575b613ee1612128866000526002602052604060002090565b82151580614096575b614038575b50613efc6115d386615d95565b916040928392835192613f2884613f1a8d8b33333360208701613e3a565b03601f198101865285610afd565b845163aa0b7db760e01b81529687948593849360048501613e82565b039134905af192831561402b575b6000928394613fcf575b5083613f90613f98927f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159596993691610be6565b908633614179565b613fc3611e90613faa6115d387615d95565b6001600160a01b0316600052600a602052604060002090565b519182523391602090a3565b613f9094507f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a1593509061401a613f9892843d8611614024575b6140128183610afd565b810190613e6c565b9550935090613f5c565b503d614008565b6140336132f2565b613f52565b61409090614056614047610b1e565b6001600160a01b039092168252565b606084811c6bffffffffffffffffffffffff908116602084015233604084015285169082015261408b613faa6115d389615d95565b613de8565b38613eef565b506001600160a01b0381161515613eea565b6040516318d86ca360e01b81526004810186905261410d90889086906060816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115614141575b600091614113575b5088613d29565b50613eca565b614134915060603d811161413a575b61412c8183610afd565b810190613dd4565b38614106565b503d614122565b6141496132f2565b6140fe565b506001614172614168876000526002602052604060002090565b5460a01c60ff1690565b1615613ec5565b91939290926001600160a01b03831690811561435e57614198856145c9565b926141a2876145c9565b9660005b85518110156142585780876141be61421e9389612eb7565b518b6141e96141e2856141db856000526008602052604060002090565b5493612eb7565b51826135ab565b91826141ff826000526008602052604060002090565b558d856201000083118061424f575b614223575b505050505050612e35565b6141a6565b6141e261423d916142378561424499613058565b93612eb7565b918d615b77565b873880808d85614213565b5083151561420e565b509091929560005b85518110156142df576142738187612eb7565b51614299612bb361428e836000526002602052604060002090565b5460e01c61ffff1690565b9060005b8b8361ffff8316106142bb57505050506142b690612e35565b614260565b906142d56142cc866142da94612eb7565b518c8386614a6d565b6145fa565b61429d565b50929650925092610b2b94826000526004602052614314826040600020906001600160a01b0316600052602052604060002090565b61431f8582546135ab565b9055604080518481526020810186905260009133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f629190a4336143ed565b6040516332db147f60e01b8152600490fd5b9081602091031261001d57516107638161048a565b909260a0926001600160a01b03610763969516835260006020840152604083015260608201528160808201520190610701565b919261076395949160a0946001600160a01b038092168552166020840152604083015260608201528160808201520190610701565b9390803b6143fd575b5050505050565b61442d9360006001600160a01b036020956040519788968795869363f23a6e6160e01b9c8d865260048601614385565b0393165af1600091816144c8575b506144a7575050600161444c6139f8565b6308c379a014614478575b614466575b38808080806143f6565b604051630a9c50b160e21b8152600490fd5b614480613a16565b8061448b5750614457565b60405162461bcd60e51b81529081906112de9060048301610752565b6001600160e01b0319161461445c57604051639fae982f60e01b8152600490fd5b6144ea91925060203d81116144f1575b6144e28183610afd565b810190614370565b903861443b565b503d6144d8565b9493919092813b61450c575b505050505050565b60006001600160a01b0360209561453b6040519889978896879463f23a6e6160e01b9d8e8752600487016143b8565b0393165af1600091816145a9575b50614588575050600161455a6139f8565b6308c379a014614575575b614466575b388080808080614504565b61457d613a16565b8061448b5750614565565b6001600160e01b0319161461456a57604051639fae982f60e01b8152600490fd5b6145c291925060203d81116144f1576144e28183610afd565b9038614549565b604051906145d682610ae2565b600182526020820160203682378251156145ee575290565b6145f6612e51565b5290565b60019061ffff809116908114612e45570190565b92939091906001600160a01b03808516908161474c57505060005b82518110156146cc5780846146416146989386612eb7565b51614656816000526008602052604060002090565b546146646141e2858c612eb7565b918261467a826000526008602052604060002090565b558a85620100008311806146c3575b61469d57505050505050612e35565b614629565b6141e26146b191614237856146b899613058565b918a615b77565b843880808a85614213565b50831515614689565b509290915b600092835b8151811015614743576146e98183612eb7565b51614704612bb361428e836000526002602052604060002090565b90865b8261ffff8216106147235750505061471e90612e35565b6146d6565b61473e906142d5614734868d612eb7565b51888a8487614d3a565b614707565b50505050509050565b80859693979594929416156000146148dc5750600095865b82518110156148cf578061477b6147d49285612eb7565b516147868289612eb7565b516147a58161479f846000526008602052604060002090565b546138d2565b90816147bb846000526008602052604060002090565b55620100008311806148c6575b6147d957505050612e35565b614764565b81614897575050867f323cd0cd659fcbea6afff32a286496077f777df422b67178fc874c0047642ea361488b8360101c6148336148186115d387615d95565b6148228382613058565b80938d61482d612ed9565b936155f5565b8d614848866000526009602052604060002090565b558d61487a8661486b8d6001600160a01b03166000526007602052604060002090565b90600052602052604060002090565b556040519081529081906020820190565b0390a35b3880806128a9565b916148c1926148a6828a613058565b6148ba6148b383836138d2565b92856135ab565b928a615b77565b61488f565b508015156147c8565b50949192509294506146d1565b9390959492506000805b8751811015614a6257806148fd61490e928a612eb7565b516201000081116149135750612e35565b6148e6565b6040516318d86ca360e01b815260048101829052614a3291906149799060609081818d81806024810103917f0000000000000000000000000000000000000000000000000000000000000000165afa918215614a55575b8892614a38575b505082613c8c565b506149d86149878289613058565b6149a5614994848b613058565b61499e878b612eb7565b51906138d2565b6149b9846000526008602052604060002090565b54906149cf856000526008602052604060002090565b5492858c615b77565b6149e28189613058565b614a006149ef838b613058565b6149f9868a612eb7565b51906135ab565b614a14836000526008602052604060002090565b5491614a2a846000526008602052604060002090565b54938b615b77565b386109b4565b614a4e9250803d1061413a5761412c8183610afd565b3880614971565b614a5d6132f2565b61496a565b5050949250906146d1565b90614a779161366b565b9290916001600160a01b0390818116158015614bb1575b60009015614ba95750935b81851615159283614b27575b505050614ab0575050565b6124e0610b2b92614adb838060081c600052600b602052600160ff604060002092161b198154169055565b60008052600c602052614b0e837f13649b2456f1b42fef0f0040b3aaeabcd21a76a0f3f5defd4f583839455116e86124e0565b6001600160a01b0316600052600c602052604060002090565b6040516327a4b8f360e21b81526000600482018190526001600160a01b038816602483015260448201929092529350602092849260649284929091165af1908115614b9c575b600091614b7e575b50388080614aa5565b614b96915060203d8111611405576113f98183610afd565b38614b75565b614ba46132f2565b614b6d565b905093614a99565b50614bd384611562836001600160a01b0316600052600c602052604060002090565b614a8e565b90614be29161366b565b9290916001600160a01b0390818116158015614d13575b60009015614d0b5750935b8185161592831593614d02575b83614c80575b505050614c22575050565b614c51816124e0610b2b94614b0e838060081c600052600b602052600160ff604060002092161b198154169055565b60008052600c6020527f13649b2456f1b42fef0f0040b3aaeabcd21a76a0f3f5defd4f583839455116e86124e0565b6040516327a4b8f360e21b81526001600160a01b038716600482015260006024820181905260448201929092529350602092849260649284929091165af1908115614cf5575b600091614cd7575b50388080614c17565b614cef915060203d8111611405576113f98183610afd565b38614cce565b614cfd6132f2565b614cc6565b60009350614c11565b905093614c04565b50614d3584611562836001600160a01b0316600052600c602052604060002090565b614bf9565b90614d479193929361366b565b9390926001600160a01b0390818116158015614e96575b60009015614e8e5750925b818116158015614e67575b60009015614e5f5750945b8184161592831593614e53575b83614dd1575b505050614d9e57505050565b610b2b92614b0e836124e06124e094614b0e838060081c600052600b602052600160ff604060002092161b198154169055565b6040516327a4b8f360e21b81526001600160a01b038681166004830152881660248201526044810191909152925060209183916064918391600091165af1908115614e46575b600091614e28575b50388080614d92565b614e40915060203d8111611405576113f98183610afd565b38614e1f565b614e4e6132f2565b614e17565b86831615159350614d8c565b905094614d7f565b50614e8985611562836001600160a01b0316600052600c602052604060002090565b614d74565b905092614d69565b50614eb885611562836001600160a01b0316600052600c602052604060002090565b614d5e565b909260809261076395948352602083015260408201528160608201520190610701565b92949394614eee8433613058565b91821561522757614f65926002614f12614168886000526002602052604060002090565b1615615191575b614f256115d387615d95565b906000614f3c886000526008602052604060002090565b54936040968796614f5788519d8e928c333360208701613e3a565b03601f1981018d528c610afd565b614f8486519b8c968795869463fe55892d60e01b865260048601614ebd565b03925af18015615184575b6000958691615161575b5080614fa68786336153a1565b614fbd612128866000526002602052604060002090565b9284614fcb6115d388615d95565b946001600160a01b0393848216615015575b505090519283525092909216913391507ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb56790602090a4565b83516314e11f2560e11b8152600481018a905284816024817f00000000000000000000000000000000000000000000000000000000000000008a165afa80600092839261513f575b5061509f575b505050936150949181967ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb5679661523f565b839250843880614fdd565b86821615158061512f575b8061511d575b15615063577ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb5679750926151106151066150ff6150f761ffff61509499979d9698168d613231565b612710900490565b809b6138d2565b998a95848461523f565b9296819850829450615063565b5061512a6115398c6135b7565b6150b0565b5061012c61ffff821611156150aa565b9092506151599150863d8811611486576114768183610afd565b90913861505d565b90508161517b9296503d8711614024576140128183610afd565b94909438614f99565b61518c6132f2565b614f8f565b6040516318d86ca360e01b8152600481018790526151f690899085906060816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561521a575b6000916151fc575b5089613d29565b50614f19565b615214915060603d811161413a5761412c8183610afd565b386151ef565b6152226132f2565b6151e7565b60405163536ae29f60e01b8152336004820152602490fd5b6040516323b872dd60e01b60208083019182526001600160a01b03948516602484015294841660448301526064820195909552929391906152838460848101613f1a565b16906040519261529284610ae2565b8484527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656485850152823b156152fd576152d8939260009283809351925af1612f54612fae565b805190816152e557505050565b82610b2b936152f8938301019101613385565b615342565b60405162461bcd60e51b815260048101869052601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b1561534957565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b9290926001600160a01b03811690811561435e576153be856145c9565b946153c8856145c9565b936153d1612ed9565b5060005b87518110156154be57806153ec61543f928a612eb7565b516153f78289612eb7565b516154108161479f846000526008602052604060002090565b9081615426846000526008602052604060002090565b55620100008311806154b5575b61544457505050612e35565b6153d5565b81614897575050867f323cd0cd659fcbea6afff32a286496077f777df422b67178fc874c0047642ea361488b8360101c6154836148186115d387615d95565b600085815260096020526040812055600061487a8661486b8d6001600160a01b03166000526007602052604060002090565b50801515615433565b509193959294909460005b8451811015615538576154dc8186612eb7565b516154f7612bb361428e836000526002602052604060002090565b9060005b8a8361ffff831610615519575050505061551490612e35565b6154c9565b906142d561552a8661553394612eb7565b518b8386614bd8565b6154fb565b509391955093915061557081615558846000526004602052604060002090565b906001600160a01b0316600052602052604060002090565b548481106155e357847fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62926155bb6000976155d5940391615558876000526004602052604060002090565b556040805194855260208501919091523393918291820190565b0390a46155e0612ed9565b50565b604051631e0d409360e11b8152600490fd5b919490949392936001600160a01b039283871692831561435e57615618836145c9565b94615622886145c9565b968382169182615812575060005b87518110156156e0576156438189612eb7565b5190615659826000526008602052604060002090565b546156676141e2838d612eb7565b908161567d856000526008602052604060002090565b558d8c620100008611806156d7575b6156a4575b5050505061569f9150612e35565b615630565b856156c76156c0876156ba6156cd999a87613058565b94612eb7565b51836135ab565b92615b77565b8038808d8c615691565b5082151561568c565b509091929593969894975b600097885b865181101561576957896157048289612eb7565b518d8b8b615722612bb361428e866000526002602052604060002090565b94925b8561ffff8516106157445750505050505061573f90612e35565b6156f0565b916142d591615757886157609695612eb7565b51918487614d3a565b8b8f8c90615725565b509594509592985095509561578c82615558866000526004602052604060002090565b54968588106155e35785610b2b98036157b384615558886000526004602052604060002090565b556157cc84615558876000526004602052604060002090565b6157d78782546135ab565b9055604080518681526020810188905233917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6291a4336144f8565b9693959290949160009a98999a95865b8651811015615976578b8b615837838a612eb7565b518a8d620100008311615858575b505050505061585390612e35565b615822565b6040516318d86ca360e01b8152600481018490526158b692916060908190839060249082907f0000000000000000000000000000000000000000000000000000000000000000165afa928315615969575b92614a3857505082613c8c565b5061590e8c6158c58382613058565b6158dc6158d28584613058565b61499e8989612eb7565b6158f0856000526008602052604060002090565b549185615907816000526008602052604060002090565b5494615b77565b61595f9261593461591f8385613058565b916149f98761592e8688613058565b92612eb7565b90615949836000526008602052604060002090565b5492615907816000526008602052604060002090565b8b8b388a8d615845565b6159716132f2565b6158a9565b509298969750929899909394506156eb565b9291909461599b60026000541415613487565b60026000556001600160a01b0394858516953387148015615ab1575b15612ccf57845184510361313d578716801561435e576159d985858a8961460e565b60005b8451811015615a64576159ef8186612eb7565b51906159fb8188612eb7565b51615a1489615558856000526004602052604060002090565b54928184106155e357611f398c615558615a58938d615a4787615a5f9a0391615558846000526004602052604060002090565b556000526004602052604060002090565b9055612e35565b6159dc565b50615aaa969792979591956040517f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb339180615aa18a8a83615ac1565b0390a433615b35565b6001600055565b50615abc338761314f565b6159b7565b9091615ad861076393604084526040840190610899565b916020818403910152610899565b93906107639593615b1991615b27946001600160a01b03809216885216602087015260a0604087015260a0860190610899565b908482036060860152610899565b916080818403910152610701565b9493919092813b615b4857505050505050565b60006001600160a01b0360209561453b6040519889978896879463bc197c8160e01b9d8e875260048701615ae6565b9293612884612884839794615c038461288460409a615c1d98998b6001600160a01b0381169d8e600090815260076020528d828220908252602052818120549d615bf6615bdc89615bd2846000526009602052604060002090565b549d8e809a613231565b946001600160a01b03166000526007602052604060002090565b9082526020522055613231565b615c178a6000526009602052604060002090565b55613231565b90808211615c2c575050505050565b8181615c5a9310615c90575b039182615c476115d387615d95565b615c4f612ed9565b928760101c916155f5565b6040519081527f323cd0cd659fcbea6afff32a286496077f777df422b67178fc874c0047642ea390602090a338808080806143f6565b615c98612e1e565b615c38565b615ca78282613058565b918215613c67576001600160a01b03615ce5615cd26000958487526009602052604087205490613231565b8386526008602052604086205490613250565b92168352600760205260408320908352602052604082205491828211600014615d1457508181106138dd570390565b91505090565b3561076381610445565b906001600160a01b039160405190602082019260ff60f81b84526bffffffffffffffffffffffff199060601b16602183015260358201527f577cbdbf32026552c0ae211272febcff3ea352b0c755f8f39b49856dcac71019605582015260558152615d8e81610a91565b5190201690565b8015615dae576136a66001600160a01b03913090615d24565b60405163afcbb58f60e01b815260006004820152602490fdfea26469706673582212201e26ce12a8883eb25212fcd0a075363a59af60deb82cf92b374cb05fc0ec714e64736f6c634300080e00330000000000000000000000009cf9f71692303aa5d175e1055fa2ace0b8f11e600000000000000000000000009cf9f71692303aa5d175e1055fa2ace0b8f11e60000000000000000000000000306977079607e8e8bb8cbe3f4fef00da8bbc33de

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000009cf9f71692303aa5d175e1055fa2ace0b8f11e600000000000000000000000009cf9f71692303aa5d175e1055fa2ace0b8f11e60000000000000000000000000306977079607e8e8bb8cbe3f4fef00da8bbc33de

-----Decoded View---------------
Arg [0] : _vhAuth (address): 0x9cf9f71692303aa5d175e1055fa2ace0b8f11e60
Arg [1] : _feeMan (address): 0x9cf9f71692303aa5d175e1055fa2ace0b8f11e60
Arg [2] : _zap (address): 0x306977079607e8e8bb8cbe3f4fef00da8bbc33de

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000009cf9f71692303aa5d175e1055fa2ace0b8f11e60
Arg [1] : 0000000000000000000000009cf9f71692303aa5d175e1055fa2ace0b8f11e60
Arg [2] : 000000000000000000000000306977079607e8e8bb8cbe3f4fef00da8bbc33de


Loading