Contract 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad 2

Txn Hash Method
Block
From
To
Value [Txn Fee]
0x407bc7ee9d234d6ca9795f7498936ddd5b5d660f45dd8b36aa5a712e2d230cf6Quartz In19517492022-09-26 18:57:4210 hrs 53 mins ago0x9d2e29be44fb2c3d7afdbeb79f59f51224373aff IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0935199
0x66bd2b7dfd0a28592d64dba7ab2643b9fbd6bb3c2effe8ab9d7873ed72658d37Quartz In19504682022-09-26 14:35:1215 hrs 15 mins ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0935188
0xdd03cd991a012adacf12b4deed181ada2c18750f2c5bb2bf72092423a60218d8Quartz In ETH19504582022-09-26 14:33:1215 hrs 17 mins ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.1 GLMR0.1230068
0x43e79f357fadc9b5fffabfdf5b4509bc557aa482b6b9d30f9b28836907b96974Quartz Out19497012022-09-26 11:59:0617 hrs 51 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0505945
0x704586633c7e9e28e95d6e3fa2c88ebbab2c201331988d1232ede2ca1cf78491Quartz Out19497002022-09-26 11:58:5417 hrs 52 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0663345
0xb9745c5090ca969c1ca04a0e82e2ae73b41d409148bdc31f116765cc646a5c3aQuartz Out19496772022-09-26 11:54:1217 hrs 56 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.1268233
0xa5e2c18f1ce39c1979c7089b73e71f2d777b432acaaec74fadf4eac60704eb31Quartz Out19496762022-09-26 11:54:0017 hrs 57 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.1411073
0xcbde71ef901def94a63409a681f50bc7eba1607d7ea16c0b22df740d593d8c43Quartz Out19496752022-09-26 11:53:4817 hrs 57 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0555756
0x5cb24b06fb7e37e9bc800892b48420c4bb84d458892ac5e00b960b8b5a3b7fcbQuartz Out19496752022-09-26 11:53:4817 hrs 57 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.1226091
0x049af8a6a584014149dea65985dea742fe170a5404cdd6cf6d3da6be12627fbdQuartz Out19496742022-09-26 11:53:3017 hrs 57 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.116016
0xa72372c093fbf4c526622469e50071d4507decc83272987154176c1a4454afc7Quartz Out19496732022-09-26 11:53:1217 hrs 57 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0849119
0x8ff4db45d30c79db176408a6f248e47b1e7aa87e64cbea4433aac5db460f32f8Quartz Out19496722022-09-26 11:53:0017 hrs 58 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0850849
0x9afe6973627a35442423c1f5fb3e8af899a30170021988f07735ce2aff84cbd4Quartz Out19496712022-09-26 11:52:4817 hrs 58 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.1046244
0xb387ef5e98e2c66781e5d53e592f8d64c862e78a727a32846a4200446baedd32Quartz Out19496702022-09-26 11:52:3017 hrs 58 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.086648
0x71e1779b755faa6c736fc0c31e2bc65619145e20c7d6461e921ee9d56cea4b17Quartz Out19496692022-09-26 11:52:1817 hrs 58 mins ago0x6e84df01250b1856d04e518a4e51b1ef1398bee4 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0977144
0x94d8a33f32b05d2e14a0e354f1279e1065e6994a5230f4d851948b49c03c06acQuartz Out19491242022-09-26 9:59:4819 hrs 51 mins ago0xe9cbcfd64802a92edbede44ecb1b2e9f0f211de8 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0833189
0x8b6194e829b7d83fa4a068648378d9de77be44492a18af09a036b626d1a75e9bQuartz Out19488782022-09-26 9:08:3620 hrs 42 mins ago0x5f45b03ecff80eb6b4d5829661fd49a241f06a33 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.1060535
0xb673b94d7da911a5706d934a1dbaa703a57d1e1a0eb785946894b666e5bc36adQuartz Out19449602022-09-25 19:38:001 day 10 hrs ago0xf9a5807e32a2c887259d365bff56f070a1d1083f IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0846653
0x971dac1daaa4c81f8b7969d07f67a502a22565c0d17e2b8e0cf042b146a19989Quartz In ETH19435212022-09-25 14:36:421 day 15 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.1 GLMR0.1544456
0xb66f1a1fd975054b2f42ebcd43e43a4c7e5d188ece85a61d104f6b30dd9b7a9cQuartz Out19389492022-09-24 22:49:062 days 7 hrs ago0xc4b4b63011d10051eaae77d6cbe7255ac93081d8 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0833199
0xab8d41903224c4420cc39a9694c652d8d8a56590aaeca193aaf3eb91e932758aQuartz In ETH19377452022-09-24 18:38:122 days 11 hrs ago0xddfa3a1ac8a259c2f9ea17fcae0f856303692dda IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad1,190 GLMR0.0633969
0xf8185d28fe7baf8a3e6d436fbfa937d380b44e4b4b54fc56088725c5e1023d70Quartz In19368562022-09-24 15:23:302 days 14 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.1246416
0x87e9430fd0a3a95b8734c8497e63e27b1359e8626493e42134dd370c53a896bfQuartz In ETH19358732022-09-24 11:59:362 days 17 hrs ago0x014d3239e6a0b9cb825527c8c365472a3e271028 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad2,430 GLMR0.1141309
0x91197b1f3b5b376fac8ed58a9328df9b41fa0449806bbafe07ef738032426a68Quartz Out19343652022-09-24 6:46:062 days 23 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0814421
0x9c291d6aed36f3deb696749bd103eb74a733b561310cbe790ac76c8e4692dda0Quartz In ETH19343622022-09-24 6:45:302 days 23 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.2 GLMR0.1534866
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xdd03cd991a012adacf12b4deed181ada2c18750f2c5bb2bf72092423a60218d819504582022-09-26 14:33:1215 hrs 17 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x56aa4fb42f77faa81b5ca0d2b3cab33f808996480.000000112477026609 GLMR
0xdd03cd991a012adacf12b4deed181ada2c18750f2c5bb2bf72092423a60218d819504582022-09-26 14:33:1215 hrs 17 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.000000112477026609 GLMR
0xdd03cd991a012adacf12b4deed181ada2c18750f2c5bb2bf72092423a60218d819504582022-09-26 14:33:1215 hrs 17 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad Moonbeam: WGLMR Token0.1 GLMR
0x43e79f357fadc9b5fffabfdf5b4509bc557aa482b6b9d30f9b28836907b9697419497012022-09-26 11:59:0617 hrs 51 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee41.707382940485122163 GLMR
0x43e79f357fadc9b5fffabfdf5b4509bc557aa482b6b9d30f9b28836907b9697419497012022-09-26 11:59:0617 hrs 51 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad1.707382940485122163 GLMR
0x704586633c7e9e28e95d6e3fa2c88ebbab2c201331988d1232ede2ca1cf7849119497002022-09-26 11:58:5417 hrs 52 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee46.942690369629969319 GLMR
0x704586633c7e9e28e95d6e3fa2c88ebbab2c201331988d1232ede2ca1cf7849119497002022-09-26 11:58:5417 hrs 52 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad6.942690369629969319 GLMR
0xb9745c5090ca969c1ca04a0e82e2ae73b41d409148bdc31f116765cc646a5c3a19496772022-09-26 11:54:1217 hrs 56 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee42.574149293594539009 GLMR
0xb9745c5090ca969c1ca04a0e82e2ae73b41d409148bdc31f116765cc646a5c3a19496772022-09-26 11:54:1217 hrs 56 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad2.574149293594539009 GLMR
0xa5e2c18f1ce39c1979c7089b73e71f2d777b432acaaec74fadf4eac60704eb3119496762022-09-26 11:54:0017 hrs 57 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee410.340094660382256226 GLMR
0xa5e2c18f1ce39c1979c7089b73e71f2d777b432acaaec74fadf4eac60704eb3119496762022-09-26 11:54:0017 hrs 57 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad10.340094660382256226 GLMR
0xcbde71ef901def94a63409a681f50bc7eba1607d7ea16c0b22df740d593d8c4319496752022-09-26 11:53:4817 hrs 57 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee41.230634091903837034 GLMR
0xcbde71ef901def94a63409a681f50bc7eba1607d7ea16c0b22df740d593d8c4319496752022-09-26 11:53:4817 hrs 57 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad1.230634091903837034 GLMR
0x5cb24b06fb7e37e9bc800892b48420c4bb84d458892ac5e00b960b8b5a3b7fcb19496752022-09-26 11:53:4817 hrs 57 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee43.24566011127287768 GLMR
0x5cb24b06fb7e37e9bc800892b48420c4bb84d458892ac5e00b960b8b5a3b7fcb19496752022-09-26 11:53:4817 hrs 57 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad3.24566011127287768 GLMR
0x049af8a6a584014149dea65985dea742fe170a5404cdd6cf6d3da6be12627fbd19496742022-09-26 11:53:3017 hrs 57 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee43.618337628121950946 GLMR
0x049af8a6a584014149dea65985dea742fe170a5404cdd6cf6d3da6be12627fbd19496742022-09-26 11:53:3017 hrs 57 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad3.618337628121950946 GLMR
0xa72372c093fbf4c526622469e50071d4507decc83272987154176c1a4454afc719496732022-09-26 11:53:1217 hrs 57 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee43.11904805173683411 GLMR
0xa72372c093fbf4c526622469e50071d4507decc83272987154176c1a4454afc719496732022-09-26 11:53:1217 hrs 57 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad3.11904805173683411 GLMR
0x8ff4db45d30c79db176408a6f248e47b1e7aa87e64cbea4433aac5db460f32f819496722022-09-26 11:53:0017 hrs 58 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee45.275721059269974661 GLMR
0x8ff4db45d30c79db176408a6f248e47b1e7aa87e64cbea4433aac5db460f32f819496722022-09-26 11:53:0017 hrs 58 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad5.275721059269974661 GLMR
0x9afe6973627a35442423c1f5fb3e8af899a30170021988f07735ce2aff84cbd419496712022-09-26 11:52:4817 hrs 58 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee45.763095896534416242 GLMR
0x9afe6973627a35442423c1f5fb3e8af899a30170021988f07735ce2aff84cbd419496712022-09-26 11:52:4817 hrs 58 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad5.763095896534416242 GLMR
0xb387ef5e98e2c66781e5d53e592f8d64c862e78a727a32846a4200446baedd3219496702022-09-26 11:52:3017 hrs 58 mins ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x6e84df01250b1856d04e518a4e51b1ef1398bee48.903244326656298962 GLMR
0xb387ef5e98e2c66781e5d53e592f8d64c862e78a727a32846a4200446baedd3219496702022-09-26 11:52:3017 hrs 58 mins ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad8.903244326656298962 GLMR
[ Download CSV Export 
Loading
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:
QuartzUniV2Zap

Compiler Version
v0.8.14+commit.80d49f37

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
default evmVersion
File 1 of 22 : 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 2 of 22 : 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 3 of 22 : 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 4 of 22 : 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 5 of 22 : 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 6 of 22 : 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 7 of 22 : 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 8 of 22 : 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 9 of 22 : 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 10 of 22 : 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 11 of 22 : 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 12 of 22 : 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 13 of 22 : 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 14 of 22 : 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 15 of 22 : 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 16 of 22 : 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 17 of 22 : 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 18 of 22 : 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 19 of 22 : 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 20 of 22 : 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 21 of 22 : 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 22 of 22 : 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;
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 1000000,
    "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/LibQuartz.sol": {
      "LibQuartz": "0xe932f92d7e09c8742fa2e5293d9ff92364c053f5"
    }
  }
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_vaultHealer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"}],"name":"VidOutOfRange","type":"error"},{"inputs":[],"name":"MINIMUM_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"},{"internalType":"contract IERC20","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"fullInvestmentIn","type":"uint256"}],"name":"estimateSwap","outputs":[{"internalType":"uint256","name":"swapAmountIn","type":"uint256"},{"internalType":"uint256","name":"swapAmountOut","type":"uint256"},{"internalType":"contract IERC20","name":"swapTokenOut","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"},{"internalType":"uint256","name":"tokenAmountOutMin","type":"uint256"},{"internalType":"contract IERC20","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"tokenInAmount","type":"uint256"}],"name":"quartzIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"},{"internalType":"uint256","name":"tokenAmountOutMin","type":"uint256"}],"name":"quartzInETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vid","type":"uint256"},{"internalType":"uint256","name":"withdrawAmount","type":"uint256"}],"name":"quartzOut","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"rescue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vaultHealer","outputs":[{"internalType":"contract IVaultHealer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a034620000a357601f620037e638819003918201601f19168301916001600160401b03831184841017620000a857808492602094604052833981010312620000a357516001600160a01b0381168103620000a3576001600160a01b03166080526040516137279081620000bf82396080518181816101f101528181610321015281816103d101528181610944015281816114070152818161193601526129780152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe60806040526004361015610023575b361561001957600080fd5b610021610a61565b005b6000803560e01c9081632201b7ba146100c657508063257d9bb8146100bd5780634f880c4b146100b45780637e2c5c19146100ab578063839006f2146100a2578063a111774d14610099578063f23a6e61146100905763f7bfff2a0361000e5761008b6108e6565b61000e565b5061008b61082b565b5061008b610605565b5061008b610345565b5061008b6102d5565b5061008b610175565b5061008b61011b565b346101085760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261010857610103602435600435611405565b604051f35b80fd5b600091031261011657565b600080fd5b50346101165760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101165760206040516103e88152f35b73ffffffffffffffffffffffffffffffffffffffff81160361011657565b50346101165760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610116576024356101b181610157565b604051907fed63953000000000000000000000000000000000000000000000000000000000825273ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000000000000000000000000000000000001660048401526004356024840152166044820152604435606482015260608160848173e932f92d7e09c8742fa2e5293d9ff92364c053f55af480156102c8575b60009081928291610292575b5060408051928352602083019390935273ffffffffffffffffffffffffffffffffffffffff1691810191909152606090f35b0390f35b905061028e92506102ba915060603d81116102c1575b6102b28183610b7e565b810190610be4565b909261025c565b503d6102a8565b6102d0610bd7565b610250565b50346101165760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261011657602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5034610116576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610116576000906004359061038682610157565b6104fd816040938451907fe8da52b000000000000000000000000000000000000000000000000000000000825273ffffffffffffffffffffffffffffffffffffffff908383600481857f0000000000000000000000000000000000000000000000000000000000000000165afa9283156105f8575b88936105c9575b5086805180947f29c23e4a00000000000000000000000000000000000000000000000000000000825281858161044060048201906000602083019252565b0392165afa9283156105bc575b889361058c575b5081831615610584575b86517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152929116908383602481855afa928315610577575b8893610548575b508787518096819582947fa9059cbb000000000000000000000000000000000000000000000000000000008452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af1801561053b575b61051157505051f35b8161053092903d10610534575b6105288183610b7e565b810190610e40565b5051f35b503d61051e565b610543610bd7565b610508565b610569919350843d8611610570575b6105618183610b7e565b810190610c05565b91386104a4565b503d610557565b61057f610bd7565b61049d565b33925061045e565b6105ad919350873d89116105b5575b6105a58183610b7e565b810190612bd2565b509138610454565b503d61059b565b6105c4610bd7565b61044d565b6105ea919350843d86116105f1575b6105e28183610b7e565b810190610bbf565b9138610402565b503d6105d8565b610600610bd7565b6103fb565b50346101165760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101165761002160443561064481610157565b6040517fdd62ed3e000000000000000000000000000000000000000000000000000000008152336004820152306024820152610779906064359060209073ffffffffffffffffffffffffffffffffffffffff8516908281604481855afa90811561081e575b600091610801575b506040517f70a0823100000000000000000000000000000000000000000000000000000000808252336004830152948482602481875afa9182156107f4575b6000926107d5575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81036107b55750839190808210156107ae57505b935b61073e6103e8861015610a6a565b61074a8530338a610db5565b60405190815230600482015291829060249082905afa9182156107a1575b600092610784575b50501015610d2a565b602435600435611932565b61079a9250803d10610570576105618183610b7e565b3880610770565b6107a9610bd7565b610768565b905061072e565b9490856107d0916107c98288961015610c14565b1015610c9f565b610730565b6107ed919250853d8711610570576105618183610b7e565b90386106f8565b6107fc610bd7565b6106f0565b6108189150833d8511610570576105618183610b7e565b386106b1565b610826610bd7565b6106a9565b50346101165760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101165760043561086781610157565b610872602435610157565b60843567ffffffffffffffff80821161011657366023830112156101165781600401359081116101165736910160240111610116576108b361028e91611107565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681529081906020820190565b5060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610116576004356109226103e8341015610a6a565b73ffffffffffffffffffffffffffffffffffffffff60046020610981610968857f0000000000000000000000000000000000000000000000000000000000000000612bfc565b73ffffffffffffffffffffffffffffffffffffffff1690565b604051928380927fad5c46480000000000000000000000000000000000000000000000000000000082525afa908115610a54575b600091610a36575b5016803b1561011657610021916040517fd0e30db000000000000000000000000000000000000000000000000000000000815260008160048134875af18015610a29575b610a10575b5060243590611932565b80610a1d610a2392610b25565b8061010b565b38610a06565b610a31610bd7565b610a01565b610a4e915060203d81116105f1576105e28183610b7e565b386109bd565b610a5c610bd7565b6109b5565b333b1561011657565b15610a7157565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f51756172747a3a20496e7369676e69666963616e7420696e70757420616d6f7560448201527f6e740000000000000000000000000000000000000000000000000000000000006064820152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff8111610b3957604052565b610b41610af5565b604052565b6080810190811067ffffffffffffffff821117610b3957604052565b6040810190811067ffffffffffffffff821117610b3957604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b3957604052565b908160209103126101165751610bd481610157565b90565b506040513d6000823e3d90fd5b908160609103126101165780519160406020830151920151610bd481610157565b90816020910312610116575190565b15610c1b57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f51756172747a3a20496e70757420746f6b656e206973206e6f7420617070726f60448201527f76656400000000000000000000000000000000000000000000000000000000006064820152fd5b15610ca657565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f51756172747a3a20496e70757420746f6b656e2068617320696e73756666696360448201527f69656e742062616c616e636500000000000000000000000000000000000000006064820152fd5b15610d3157565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f51756172747a3a204665652d6f6e2d7472616e736665722f7265666c6563742060448201527f746f6b656e73206e6f742079657420737570706f7274656400000000000000006064820152fd5b9092610e3193604051937f23b872dd00000000000000000000000000000000000000000000000000000000602086015273ffffffffffffffffffffffffffffffffffffffff809216602486015216604484015260648301526064825260a0820182811067ffffffffffffffff821117610e33575b604052610ee3565b565b610e3b610af5565b610e29565b90816020910312610116575180151581036101165790565b15610e5f57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b73ffffffffffffffffffffffffffffffffffffffff169060405190610f0782610b62565b6020928383527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656484840152803b15610f7e5760008281928287610f599796519301915af1610f53610fdc565b90611048565b80519081610f6657505050565b82610e3193610f79938301019101610e40565b610e58565b606484604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b3d15611043573d9067ffffffffffffffff8211611036575b6040519161102a60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610b7e565b82523d6000602084013e565b61103e610af5565b610ff4565b606090565b90919015611054575090565b8151156110645750805190602001fd5b604051907f08c379a00000000000000000000000000000000000000000000000000000000082528160208060048301528251928360248401526000915b8483106110ee575050601f836044947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093116110e1575b01168101030190fd5b60008582860101526110d8565b81830181015186840160440152859350918201916110a1565b73ffffffffffffffffffffffffffffffffffffffff30911603611148577ff23a6e610000000000000000000000000000000000000000000000000000000090565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f51756172747a3a20496d70726f7065722045524331313535207472616e73666560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152fd5b60209067ffffffffffffffff81116111e6575b60051b0190565b6111ee610af5565b6111df565b9060209182818303126101165780519067ffffffffffffffff82116101165701601f9282848301121561011657815161122b816111cc565b9460409061123b82519788610b7e565b828752838088019360071b86010194868611610116578401925b858410611266575050505050505090565b86828501121561011657825161127b81610b46565b80608086018981116101165791879287949294905b8082106112a857505060809350815201930192611255565b8151865294840194899490910190611290565b9060609173ffffffffffffffffffffffffffffffffffffffff6040820192168152602092816040858094015285518094520193019160005b828110611301575050505090565b8351855293810193928101926001016112f3565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602090805115611353570190565b61135b611315565b0190565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211811515166113c0570290565b6113c861135f565b0290565b81156113d6570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f00000000000000000000000000000000000000000000000000000000000000006114308282612d0f565b939294915094801515600014611847576114a09073ffffffffffffffffffffffffffffffffffffffff8416906114ba61146885612ba2565b604051907fd1741f680000000000000000000000000000000000000000000000000000000082528180600097889333600484016112bb565b0381875afa90811561183a575b8591611818575b50611345565b5180518211156117f6576020915001515b813b156117f2576040517ff242432a00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101859052606481019190915260a06084820152600060a48201529190829060c490829084905af180156117e5575b6117d2575b505b73ffffffffffffffffffffffffffffffffffffffff80921692833b1561011657829160405180957f744fb6ca00000000000000000000000000000000000000000000000000000000825260009687918183816115d3886004830160809181527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602082015260606040820152600060608201520190565b03925af180156117c5575b6117b2575b508462010000821161179f575b5050604051947fad5c4648000000000000000000000000000000000000000000000000000000008652856004816020968794165afa948515611792575b8495611773575b5015611766576040517f0dfe1681000000000000000000000000000000000000000000000000000000008152918582168184600481845afa938415611759575b8594611733575b509080600492604051938480927fd21220a70000000000000000000000000000000000000000000000000000000082525afa948515611726575b94611707575b505080841680828416141591826116fa575b5050156116e257505050610e31903390613181565b826116f5916116f5610e31963090613181565b61276b565b84161415905038806116cd565b61171e929450803d106105f1576105e28183610b7e565b9138806116bb565b61172e610bd7565b6116b5565b8291945091611750600493823d84116105f1576105e28183610b7e565b9491925061167b565b611761610bd7565b611674565b915050610e31921661276b565b61178b919550833d85116105f1576105e28183610b7e565b9338611634565b61179a610bd7565b61162d565b6117ab9160101c611405565b38846115f0565b80610a1d6117bf92610b25565b386115e3565b6117cd610bd7565b6115de565b80610a1d6117df92610b25565b3861153a565b6117ed610bd7565b611535565b8280fd5b604061180a6118139360608401519061138f565b910151906113cc565b6114cb565b61183491503d8087833e61182c8183610b7e565b8101906111f3565b386114b4565b611842610bd7565b6114ad565b506040517efdd58e0000000000000000000000000000000000000000000000000000000081523060048201526024810182905260208160448173ffffffffffffffffffffffffffffffffffffffff87165afa9081156118d4575b6000916118b6575b5061153c575b5050505050565b6118ce915060203d8111610570576105618183610b7e565b386118a9565b6118dc610bd7565b6118a1565b51906dffffffffffffffffffffffffffff8216820361011657565b9081606091031261011657611910816118e1565b91604061191f602084016118e1565b92015163ffffffff811681036101165790565b91907f000000000000000000000000000000000000000000000000000000000000000061195f8482612d0f565b91509591936040968796875180937fad5c464800000000000000000000000000000000000000000000000000000000825260209889856004809d819a848873ffffffffffffffffffffffffffffffffffffffff9d8e9d8e8a165afa9788156126c0575b6000986126a1575b50156124fc57898316958151957f0dfe1681000000000000000000000000000000000000000000000000000000008752858785818b5afa9687156124ef575b6000976124d0575b508b8351987fd21220a7000000000000000000000000000000000000000000000000000000008a52878a8781845afa998a156124c3575b60009a612476575b5087948c868b9996611c539f968f9c968f969b6116f59f9c988f99611c289a8f85859e16958681831614600014611f715750505091611ba59381926060611b38958c51948580927f0902f1ac0000000000000000000000000000000000000000000000000000000082525afa918215611f64575b6000938493611f2b575b508b517f70a08231000000000000000000000000000000000000000000000000000000008152309581019586529394849190829081906020015b03915afa918215611f1e575b600092611eff575b506dffffffffffffffffffffffffffff80911692169085612f0e565b86517f64eb8ac900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9384168b820190815260208101929092529583166040820152918716606083015260808201929092528391829160a00190565b038173e932f92d7e09c8742fa2e5293d9ff92364c053f55af48015611ef2575b611ed5575b505b517f198d12f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff948516938101938452958416602084015290921660408201528391829160600190565b038173e932f92d7e09c8742fa2e5293d9ff92364c053f55af48015611ec8575b611eab575b5061276b565b1692611c5e84612914565b84875180957f70a082310000000000000000000000000000000000000000000000000000000082528180611cb1308c830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03915afa938415611e9e575b600094611e7f575b501691823b15610116576000611d20918751809381927faa0b7db7000000000000000000000000000000000000000000000000000000008352868a840190916080928252602082015260606040820152600060608201520190565b038183875af18015611e72575b611e5f575b5084517efdd58e00000000000000000000000000000000000000000000000000000000815230858201908152602081018390528490829081906040010381865afa938415611e52575b600094611e33575b5050813b156101165760008094611e079651968795869485937ff242432a000000000000000000000000000000000000000000000000000000008552339030908601929060c0949273ffffffffffffffffffffffffffffffffffffffff80921685521660208401526040830152606082015260a06080820152600060a08201520190565b03925af18015611e26575b611e195750565b80610a1d610e3192610b25565b611e2e610bd7565b611e12565b611e4a929450803d10610570576105618183610b7e565b913880611d83565b611e5a610bd7565b611d7b565b80610a1d611e6c92610b25565b38611d32565b611e7a610bd7565b611d2d565b611e97919450853d8711610570576105618183610b7e565b9238611cc5565b611ea6610bd7565b611cbd565b611ec1908d803d10610570576105618183610b7e565b5038611c4d565b611ed0610bd7565b611c48565b611eeb90873d8911610570576105618183610b7e565b5038611bca565b611efa610bd7565b611bc5565b611f17919250883d8a11610570576105618183610b7e565b9038611b1c565b611f26610bd7565b611b14565b611b089450611f5291935060603d8111611f5d575b611f4a8183610b7e565b8101906118fc565b509093909290611ace565b503d611f40565b611f6c610bd7565b611ac4565b92935090918b1685036120fe57505091612007916060612074948a51928380927f0902f1ac0000000000000000000000000000000000000000000000000000000082525afa9081156120f1575b878d60009283946120c9575b508b517f70a08231000000000000000000000000000000000000000000000000000000008152309181019182529394849182908190602001611b08565b86517f64eb8ac900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9384168b820190815260208101929092529583166040820152918c16606083015260808201929092528391829160a00190565b038173e932f92d7e09c8742fa2e5293d9ff92364c053f55af480156120bc575b61209f575b50611bcc565b6120b590873d8911610570576105618183610b7e565b5038612099565b6120c4610bd7565b612094565b611b0894506120e791935060603d8111611f5d57611f4a8183610b7e565b5092909293611fca565b6120f9610bd7565b611fbe565b949291509592946122bd9795848a5180927f70a08231000000000000000000000000000000000000000000000000000000008252818061215d3089830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03915afa908115612469575b60009161244c575b5060011c948482612183868a856132eb565b15612386578b517f64eb8ac900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808816878301908152602081018b9052818c166040820152941660608501526080840191909152918290819060a001038173e932f92d7e09c8742fa2e5293d9ff92364c053f55af48015612379575b61235c575b505b61222383878c6132eb565b156122e55788517f64eb8ac900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff93841692810192835260208301959095528286166040830152918916606082015260808101919091528290819060a0015b038173e932f92d7e09c8742fa2e5293d9ff92364c053f55af480156122d8575b6122c2575061276b565b611bcc565b611ec1908a3d8c11610570576105618183610b7e565b6122e0610bd7565b6122b3565b88517f097693e600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff938416928101928352602083019590955282861660408301528287166060830152918916608082015260a08101919091528290819060c001612293565b61237290853d8711610570576105618183610b7e565b5038612216565b612381610bd7565b612211565b8b517f097693e600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808816878301908152602081018b9052818c166040820152818d1660608201529416608085015260a0840191909152918290819060c001038173e932f92d7e09c8742fa2e5293d9ff92364c053f55af4801561243f575b612422575b50612218565b61243890853d8711610570576105618183610b7e565b503861241c565b612447610bd7565b612417565b6124639150853d8711610570576105618183610b7e565b38612171565b612471610bd7565b612169565b869a5094889c898b99968a99968f9d96839a8f9e3d86116124bc575b61249c8183610b7e565b81016124a791610bbf565b9f50965096995096999b50509c509497611a50565b503d612492565b6124cb610bd7565b611a48565b6124e8919750863d88116105f1576105e28183610b7e565b9538611a11565b6124f7610bd7565b611a09565b6125f397959492508051947f70a082310000000000000000000000000000000000000000000000000000000086528987898d888a8061255a308b830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03818487165afa998a15612694575b60009a61266d575b50878361257f9286166132eb565b156125f85750612293935197889687967f64eb8ac9000000000000000000000000000000000000000000000000000000008852870191909360809396959460a084019773ffffffffffffffffffffffffffffffffffffffff9384809316865260208601521660408401521660608201520152565b611c53565b93517f097693e600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff96871695810195865260208601989098528516604085015291841660608401529216608082015260a08101919091528290819060c001612293565b61257f919a508361268b8a928c8d3d10610570576105618183610b7e565b9b925050612571565b61269c610bd7565b612569565b6126b9919850853d87116105f1576105e28183610b7e565b96386119ca565b6126c8610bd7565b6119c2565b604051906000602083019280841067ffffffffffffffff8511176126f9575b8360405281815292369037565b612701610af5565b6126ec565b1561270d57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f51756172747a3a20455448207472616e73666572206661696c656400000000006044820152fd5b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529173ffffffffffffffffffffffffffffffffffffffff8281169291602085602481875afa9485156128a0575b600095612880575b5084156118af57169182036128735750803b15610116576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092526000908290602490829084905af18015612866575b612853575b50610e316000804761283a6126cd565b9060208251920190335af161284d610fdc565b50612706565b80610a1d61286092610b25565b3861282a565b61286e610bd7565b612825565b610e3192915033906128ad565b61289991955060203d8111610570576105618183610b7e565b93386127cb565b6128a8610bd7565b6127c3565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff929092166024830152604480830193909352918152610e319161290f606483610b7e565b610ee3565b73ffffffffffffffffffffffffffffffffffffffff908181169081600052600060205260ff604060002054161561294a57505050565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000009390931673ffffffffffffffffffffffffffffffffffffffff81166024850152610e3193612ac193612a9b92612a6992612a95926129f39190602090829060449082905afa908115612b0a575b600091612aec575b5015612b17565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff90911660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60448201529182906064820190565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610b7e565b82610ee3565b73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b612b04915060203d8111610570576105618183610b7e565b386129ec565b612b12610bd7565b6129e4565b15612b1e57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152fd5b9060405191612bb083610b62565b60018352602083016020368237835115612bc75752565b612bcf611315565b52565b91908260409103126101165760208251612beb81610157565b92015161ffff811681036101165790565b73ffffffffffffffffffffffffffffffffffffffff612c1f60049360209361360f565b16604051928380927ff887ea400000000000000000000000000000000000000000000000000000000082525afa908115612c77575b600091612c5f575090565b610bd4915060203d81116105f1576105e28183610b7e565b612c7f610bd7565b612c54565b15612c8b57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f51756172747a3a2054686973207661756c742063616e6e6f74206265207a617060448201527f70656400000000000000000000000000000000000000000000000000000000006064820152fd5b600091612d1b9161360f565b9073ffffffffffffffffffffffffffffffffffffffff90818316936040517ff887ea4000000000000000000000000000000000000000000000000000000000815260209384826004818a5afa918215612ed6575b600092612eb7575b50806004868499604051928380927fd23e04800000000000000000000000000000000000000000000000000000000082525afa908115612eaa575b600091612e8d575b501694604051927fc45a01550000000000000000000000000000000000000000000000000000000080855282856004818b5afa60009581612e6e575b50612e02575050505050565b839495965083916004849260405194859384928352165afa918215612e61575b600092612e44575b50501691161490612e3a82612c84565b38808080806118af565b612e5a9250803d106105f1576105e28183610b7e565b3880612e2a565b612e69610bd7565b612e22565b612e86919650843d86116105f1576105e28183610b7e565b9438612df6565b612ea49150873d89116105f1576105e28183610b7e565b38612dba565b612eb2610bd7565b612db2565b612ecf919250853d87116105f1576105e28183610b7e565b9038612d77565b612ede610bd7565b612d6f565b81198111612eef570190565b61135b61135f565b818110612f02570390565b612f0a61135f565b0390565b73ffffffffffffffffffffffffffffffffffffffff613031929361302c92610bd4968660011c9384921692604051917f054d50d400000000000000000000000000000000000000000000000000000000835260209482868580612f888684988b600485016040919493926060820195825260208201520152565b0381855afa948515613094575b600095613069575b5084612fb0612ffe9697612fb693612ee3565b93612ef7565b916040518095819482937fad615dec0000000000000000000000000000000000000000000000000000000084528b600485016040919493926060820195825260208201520152565b03915afa92831561305c575b600093613037575b505061302283613027939461138f565b61138f565b6113cc565b6134c5565b90612ef7565b6130279350613022918161305692903d10610570576105618183610b7e565b92613012565b613064610bd7565b61300a565b612ffe955090612fb061308b612fb693873d8911610570576105618183610b7e565b96505090612f9d565b61309c610bd7565b612f95565b9190826040910312610116576020825192015190565b156130be57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f51756172747a3a20494e53554646494349454e545f415f414d4f554e540000006044820152fd5b1561312357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f51756172747a3a20494e53554646494349454e545f425f414d4f554e540000006044820152fd5b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529173ffffffffffffffffffffffffffffffffffffffff9190911690602083602481855afa9283156132de575b6000936132be575b5082156132b957613250826040926131ff610e3196836000976128ad565b83519485809481937f89afcb440000000000000000000000000000000000000000000000000000000083526004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03925af180156132ac575b600091829161327c575b506132756103e8809310156130b7565b101561311c565b905061329f915060403d81116132a5575b6132978183610b7e565b8101906130a1565b38613265565b503d61328d565b6132b4610bd7565b61325b565b505050565b6132d791935060203d8111610570576105618183610b7e565b91386131e1565b6132e6610bd7565b6131d9565b6133a790604051927fc45a01550000000000000000000000000000000000000000000000000000000084526020938492838260048173ffffffffffffffffffffffffffffffffffffffff809b165afa9182156134b8575b600092613499575b506040517fe6a4390500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201529216602483015290928391908290879082906044820190565b0392165afa91821561348c575b60009261346f575b5050168015613469576060600491604051928380927f0902f1ac0000000000000000000000000000000000000000000000000000000082525afa90811561345c575b600090819261343a575b506103e890816dffffffffffffffffffffffffffff80921611928361342e575b50505090565b16119050388080613428565b9050613454915060603d8111611f5d57611f4a8183610b7e565b509038613408565b613464610bd7565b6133fe565b50600090565b6134859250803d106105f1576105e28183610b7e565b38806133bc565b613494610bd7565b6133b4565b6134b1919250843d86116105f1576105e28183610b7e565b903861334a565b6134c0610bd7565b613342565b8015613469576001817001000000000000000000000000000000008110156135f8575b61358f61358561357b61357161356761355d6135a097600888680100000000000000006135999a10156135eb575b6401000000008110156135de575b620100008110156135d2575b6101008110156135c6575b60108110156135b9575b10156135b1575b613556818b6113cc565b0160011c90565b613556818a6113cc565b61355681896113cc565b61355681886113cc565b61355681876113cc565b61355681866113cc565b61355681856113cc565b80926113cc565b808210156135ac575090565b905090565b60011b61354c565b60041c9160021b91613545565b811c9160041b9161353b565b60101c91811b91613530565b60201c9160101b91613524565b60401c9160201b91613516565b50680100000000000000009050608082901c6134e8565b81156136c05773ffffffffffffffffffffffffffffffffffffffff91604051907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060208301937fff00000000000000000000000000000000000000000000000000000000000000855260601b16602183015260358201527f577cbdbf32026552c0ae211272febcff3ea352b0c755f8f39b49856dcac710196055820152605581526136b981610b46565b5190201690565b60246040517fafcbb58f00000000000000000000000000000000000000000000000000000000815260006004820152fdfea2646970667358221220e699409d73de0f9e501f90ace3b94af28f03ea82095d3a4f9d975759b15eabf364736f6c634300080e003300000000000000000000000014e1bc2da67de9e9efd7116d9d2f6801374c32a7

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

00000000000000000000000014e1bc2da67de9e9efd7116d9d2f6801374c32a7

-----Decoded View---------------
Arg [0] : _vaultHealer (address): 0x14e1bc2da67de9e9efd7116d9d2f6801374c32a7

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000014e1bc2da67de9e9efd7116d9d2f6801374c32a7


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