Contract 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad 2

Txn Hash Method
Block
From
To
Value [Txn Fee]
0xa5bd1492c88233bd8ca2a464d8ab60fc6d57bd60edb295de25c85f7f125cdd1fQuartz In ETH24272652022-12-03 1:55:24370 days 11 hrs ago0xb2a28925eb734ecaa1844c5e0f9b1ac439ad1834 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad1 GLMR0.08012835
0x7a43e9cebcc42e96fd992809188acc45f4e7329caeaa6eb88e48b656a2668552Quartz In ETH24272522022-12-03 1:52:48370 days 11 hrs ago0xb2a28925eb734ecaa1844c5e0f9b1ac439ad1834 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad1 GLMR0.132618151
0x7bad1ef2e31302548022881591f3fe853271bbe51c6bf9871c7196fb05645677Quartz In ETH23487582022-11-22 0:18:36381 days 13 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.1 GLMR0.1233114
0x08b0ade0a8adc98f076ef18e4b1efa8771edc30cd193e74a437bf16bbe813b45Quartz In ETH23440962022-11-21 8:33:42382 days 5 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad153 GLMR0.1305959
0x62d123e4fbeb442d45197315a32ba9e67cd933fdf8a8cc657170207603bdb864Quartz In ETH23367242022-11-20 7:42:30383 days 5 hrs ago0x0bbfd3de3d8ab1c56e406fd2ccfac2bc1886c991 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad295 GLMR0.1061373
0x9dfb94add92185f7f589d1d889f2bb2cc7f5022cfd464c37cededca2e4a9b5f1Quartz Out23145972022-11-17 4:45:18386 days 8 hrs ago0xb1c398ddf9f45eacdb42487347950a54cb0fb02f IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0380168
0xf24ff9d9f40882229f0f09e0d6e34339c4df904494e77df84f7df8bde8ba53dfQuartz Out23145952022-11-17 4:44:54386 days 8 hrs ago0xb1c398ddf9f45eacdb42487347950a54cb0fb02f IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.1055344
0x1c3f37511a89298060888f0c1cd035cb0a4a3852c3f29804021a81a577d4284eQuartz Out23065342022-11-16 1:27:00387 days 12 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0660134
0x585a7b4d1a6350008b789c3e035645979651d128e41cb6dbb237e27f92df3a56Quartz In ETH23065322022-11-16 1:26:36387 days 12 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.115924497879991 GLMR0.1294487
0x3c6528ddbeaf36a8f40fc400e4a5d6ff5adb1927a5a7c4d701edfa7bed9e6903Quartz Out22968292022-11-14 16:34:24388 days 21 hrs ago0xcc801937cfe4459aa88065af325585cac5ddcf6e IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.1010627
0x1f799bea9887c67addc237771ff157111a41346a6f13abc8c710b760e6e8fe8aQuartz Out22961852022-11-14 14:22:36388 days 23 hrs ago0xff4f7b726ee86f810531d2842b3bdd8a512db594 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.04834544
0x9342e52de9c937daec0a6d535995068f3290e8d9fd3fffaced0baffd5bf9cf55Quartz In ETH22845752022-11-12 22:48:18390 days 14 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad50 GLMR0.1283937
0x94c791d1cb55fde750a2928d0ea86d387a42cd77f88f24b0731b0a9b1b9ffc1dQuartz In22669982022-11-10 11:04:36393 days 2 hrs ago0x0bbfd3de3d8ab1c56e406fd2ccfac2bc1886c991 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.058944
0xb283cc49ae17736f31a40da8d0e7545922e8f364856a563910fc48e974c74f95Quartz In ETH22669922022-11-10 11:03:24393 days 2 hrs ago0x0bbfd3de3d8ab1c56e406fd2ccfac2bc1886c991 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad18 GLMR0.1050362
0xa720ebfefb0ec29269ae3d0c53f1071f0e305d9e6a4e7019d2ea9269e2aab8b8Quartz In22669862022-11-10 11:02:12393 days 2 hrs ago0x0bbfd3de3d8ab1c56e406fd2ccfac2bc1886c991 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0939872
0x2b2e9a8fd0be572dc9efc76ef7cea5294e1466a91fde29305bbdd6e8ec69bf8eQuartz In22669662022-11-10 10:58:06393 days 2 hrs ago0x0bbfd3de3d8ab1c56e406fd2ccfac2bc1886c991 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.1156935
0x91618ed1741e0fc15650f37f792c119e586d5ce43b36ffd412f99415a25b99fcQuartz In22419942022-11-06 21:37:30396 days 16 hrs ago0xcedfa3fdd3c7c3c233fb1a0558d94e8621afab50 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0523224
0x90c68eec82a81e949e5585b9a9f3df6a0398bff3bafaa0e4c39244f5942afa22Quartz Out22202892022-11-03 19:39:18399 days 18 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0644392
0x3aed8722292a6df8c1117be221c1456898a9f49bb2599d47780b152ff5dd5a21Quartz In ETH22202842022-11-03 19:38:18399 days 18 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad1 GLMR0.155858
0xefa4b751e84bceabd431e9604d34921393d8dafe4e6b69cb1fbb89709dad7c49Quartz In22169542022-11-03 8:15:12400 days 5 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.1581356
0xff5dd0547f66f7d291582582de76d737d8c389ad893a9de3fce991f19fc95a43Quartz In ETH22147602022-11-03 0:46:48400 days 12 hrs ago0x56aa4fb42f77faa81b5ca0d2b3cab33f80899648 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad166 GLMR0.0779767
0xbb3f7f960f11bae2ae9051663e28dd4e68a72cc439a2251cae75f630153ee53bQuartz In ETH22023452022-11-01 6:36:06402 days 7 hrs ago0x0bbfd3de3d8ab1c56e406fd2ccfac2bc1886c991 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad11 GLMR0.1043957
0xcf12aec89aec8396397f866f8c5ee49f1e796c849eab19e43632e80e9267587aQuartz In22023422022-11-01 6:35:30402 days 7 hrs ago0x0bbfd3de3d8ab1c56e406fd2ccfac2bc1886c991 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0939856
0x09f53454c35ffa010caed0fe862eece63ce9babe7bf846df50d9c9f86c36d3b7Quartz In22023282022-11-01 6:32:42402 days 7 hrs ago0x0bbfd3de3d8ab1c56e406fd2ccfac2bc1886c991 IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.113461
0x0073d0fd9b542122a4b509d0cc9e5b9442d06f3d25ff8dd1e3d349059fdcd671Quartz Out21943312022-10-31 3:32:12403 days 10 hrs ago0x9d2e29be44fb2c3d7afdbeb79f59f51224373aff IN  0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0 GLMR0.0386189
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xa5bd1492c88233bd8ca2a464d8ab60fc6d57bd60edb295de25c85f7f125cdd1f24272652022-12-03 1:55:24370 days 11 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0xb2a28925eb734ecaa1844c5e0f9b1ac439ad18340.000001123363327432 GLMR
0xa5bd1492c88233bd8ca2a464d8ab60fc6d57bd60edb295de25c85f7f125cdd1f24272652022-12-03 1:55:24370 days 11 hrs ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.000001123363327432 GLMR
0xa5bd1492c88233bd8ca2a464d8ab60fc6d57bd60edb295de25c85f7f125cdd1f24272652022-12-03 1:55:24370 days 11 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad Moonbeam: WGLMR Token1 GLMR
0x7a43e9cebcc42e96fd992809188acc45f4e7329caeaa6eb88e48b656a266855224272522022-12-03 1:52:48370 days 11 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0xb2a28925eb734ecaa1844c5e0f9b1ac439ad18340.00000112336340415 GLMR
0x7a43e9cebcc42e96fd992809188acc45f4e7329caeaa6eb88e48b656a266855224272522022-12-03 1:52:48370 days 11 hrs ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.00000112336340415 GLMR
0x7a43e9cebcc42e96fd992809188acc45f4e7329caeaa6eb88e48b656a266855224272522022-12-03 1:52:48370 days 11 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad Moonbeam: WGLMR Token1 GLMR
0x7bad1ef2e31302548022881591f3fe853271bbe51c6bf9871c7196fb0564567723487582022-11-22 0:18:36381 days 13 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x56aa4fb42f77faa81b5ca0d2b3cab33f808996480.000000112484914552 GLMR
0x7bad1ef2e31302548022881591f3fe853271bbe51c6bf9871c7196fb0564567723487582022-11-22 0:18:36381 days 13 hrs ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.000000112484914552 GLMR
0x7bad1ef2e31302548022881591f3fe853271bbe51c6bf9871c7196fb0564567723487582022-11-22 0:18:36381 days 13 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad Moonbeam: WGLMR Token0.1 GLMR
0x08b0ade0a8adc98f076ef18e4b1efa8771edc30cd193e74a437bf16bbe813b4523440962022-11-21 8:33:42382 days 5 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x56aa4fb42f77faa81b5ca0d2b3cab33f808996480.000138136803540765 GLMR
0x08b0ade0a8adc98f076ef18e4b1efa8771edc30cd193e74a437bf16bbe813b4523440962022-11-21 8:33:42382 days 5 hrs ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.000138136803540765 GLMR
0x08b0ade0a8adc98f076ef18e4b1efa8771edc30cd193e74a437bf16bbe813b4523440962022-11-21 8:33:42382 days 5 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad Moonbeam: WGLMR Token153 GLMR
0x62d123e4fbeb442d45197315a32ba9e67cd933fdf8a8cc657170207603bdb86423367242022-11-20 7:42:30383 days 5 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x0bbfd3de3d8ab1c56e406fd2ccfac2bc1886c9910.000127535402866948 GLMR
0x62d123e4fbeb442d45197315a32ba9e67cd933fdf8a8cc657170207603bdb86423367242022-11-20 7:42:30383 days 5 hrs ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.000127535402866948 GLMR
0x62d123e4fbeb442d45197315a32ba9e67cd933fdf8a8cc657170207603bdb86423367242022-11-20 7:42:30383 days 5 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad Moonbeam: WGLMR Token295 GLMR
0x9dfb94add92185f7f589d1d889f2bb2cc7f5022cfd464c37cededca2e4a9b5f123145972022-11-17 4:45:18386 days 8 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0xb1c398ddf9f45eacdb42487347950a54cb0fb02f16.761815907653324228 GLMR
0x9dfb94add92185f7f589d1d889f2bb2cc7f5022cfd464c37cededca2e4a9b5f123145972022-11-17 4:45:18386 days 8 hrs ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad16.761815907653324228 GLMR
0xf24ff9d9f40882229f0f09e0d6e34339c4df904494e77df84f7df8bde8ba53df23145952022-11-17 4:44:54386 days 8 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0xb1c398ddf9f45eacdb42487347950a54cb0fb02f28.569617453444733359 GLMR
0xf24ff9d9f40882229f0f09e0d6e34339c4df904494e77df84f7df8bde8ba53df23145952022-11-17 4:44:54386 days 8 hrs ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad28.569617453444733359 GLMR
0x1c3f37511a89298060888f0c1cd035cb0a4a3852c3f29804021a81a577d4284e23065342022-11-16 1:27:00387 days 12 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x56aa4fb42f77faa81b5ca0d2b3cab33f8089964825.752661898361031954 GLMR
0x1c3f37511a89298060888f0c1cd035cb0a4a3852c3f29804021a81a577d4284e23065342022-11-16 1:27:00387 days 12 hrs ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad25.752661898361031954 GLMR
0x585a7b4d1a6350008b789c3e035645979651d128e41cb6dbb237e27f92df3a5623065322022-11-16 1:26:36387 days 12 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0x56aa4fb42f77faa81b5ca0d2b3cab33f808996480.000000130394274991 GLMR
0x585a7b4d1a6350008b789c3e035645979651d128e41cb6dbb237e27f92df3a5623065322022-11-16 1:26:36387 days 12 hrs ago Moonbeam: WGLMR Token 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0.000000130394274991 GLMR
0x585a7b4d1a6350008b789c3e035645979651d128e41cb6dbb237e27f92df3a5623065322022-11-16 1:26:36387 days 12 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad Moonbeam: WGLMR Token0.115924497879991906 GLMR
0x3c6528ddbeaf36a8f40fc400e4a5d6ff5adb1927a5a7c4d701edfa7bed9e690322968292022-11-14 16:34:24388 days 21 hrs ago 0x9909d64a63b0f11b8eed3d4d7313efeda8be5dad0xcc801937cfe4459aa88065af325585cac5ddcf6e322.058375213940777466 GLMR
[ Download CSV Export 
Index Block
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
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.