ETH Price: $3,385.36 (-1.52%)
Gas: 2 Gwei

Contract

0x4c406C068106375724275Cbff028770C544a1333
 
Transaction Hash
Method
Block
From
To
Value
Deposit201173232024-06-18 8:00:4711 days ago1718697647IN
Sandclock Yield ETH: scWETHv2 Token
4 ETH0.00095444.86013812
Supply And Borro...200842442024-06-13 16:58:4715 days ago1718297927IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.0092326321.35256705
Rebalance200842402024-06-13 16:57:5915 days ago1718297879IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.0180755121.58955986
Deposit200832522024-06-13 13:38:4715 days ago1718285927IN
Sandclock Yield ETH: scWETHv2 Token
40 ETH0.0028720414.58949089
Redeem200719272024-06-11 23:39:4717 days ago1718149187IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.00147717
Deposit200708202024-06-11 19:57:2317 days ago1718135843IN
Sandclock Yield ETH: scWETHv2 Token
0.001 ETH0.0026571712.44725846
Supply And Borro...200682752024-06-11 11:26:1117 days ago1718105171IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.0052123512.59513436
Rebalance200682752024-06-11 11:26:1117 days ago1718105171IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.0096432812.59513436
Deposit200668242024-06-11 6:34:1118 days ago1718087651IN
Sandclock Yield ETH: scWETHv2 Token
40 ETH0.001186135.55633777
Deposit200408622024-06-07 15:31:4721 days ago1717774307IN
Sandclock Yield ETH: scWETHv2 Token
3.9 ETH0.0048526724.69668453
Redeem199429402024-05-24 23:12:2335 days ago1716592343IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.00120225.61406452
Deposit199394392024-05-24 11:27:2335 days ago1716550043IN
Sandclock Yield ETH: scWETHv2 Token
11 ETH0.001200826.11532841
Deposit199226102024-05-22 2:59:5938 days ago1716346799IN
Sandclock Yield ETH: scWETHv2 Token
0.65 ETH0.001374776.99664861
Deposit199225302024-05-22 2:43:5938 days ago1716345839IN
Sandclock Yield ETH: scWETHv2 Token
0.2 ETH0.001704957.98235242
Supply And Borro...198887222024-05-17 9:12:5943 days ago1715937179IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.0051081611.82454409
Rebalance198887162024-05-17 9:11:4743 days ago1715937107IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.0101462112.4078269
Transfer198829752024-05-16 13:56:3543 days ago1715867795IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.000205896.99258193
Deposit198829672024-05-16 13:54:5943 days ago1715867699IN
Sandclock Yield ETH: scWETHv2 Token
1.5 ETH0.001527927.15740184
Supply And Borro...198764372024-05-15 16:00:4744 days ago1715788847IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.0075419617.45836541
Rebalance198764322024-05-15 15:59:4744 days ago1715788787IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.0127634816.29676837
Supply And Borro...198638232024-05-13 21:36:5946 days ago1715636219IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.003732738.64064647
Rebalance198638182024-05-13 21:35:4746 days ago1715636147IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.006333968.26789673
Redeem198503002024-05-12 0:14:1148 days ago1715472851IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.000820583.65962258
Deposit198029542024-05-05 9:19:3555 days ago1714900775IN
Sandclock Yield ETH: scWETHv2 Token
0.1 ETH0.001290735.69189204
Supply And Borro...197881682024-05-03 7:42:2357 days ago1714722143IN
Sandclock Yield ETH: scWETHv2 Token
0 ETH0.004306349.97485908
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To Value
201173232024-06-18 8:00:4711 days ago1718697647
Sandclock Yield ETH: scWETHv2 Token
4 ETH
200842402024-06-13 16:57:5915 days ago1718297879
Sandclock Yield ETH: scWETHv2 Token
397.05613471 ETH
200842402024-06-13 16:57:5915 days ago1718297879
Sandclock Yield ETH: scWETHv2 Token
397.05613471 ETH
200832522024-06-13 13:38:4715 days ago1718285927
Sandclock Yield ETH: scWETHv2 Token
40 ETH
200708202024-06-11 19:57:2317 days ago1718135843
Sandclock Yield ETH: scWETHv2 Token
0.001 ETH
200682752024-06-11 11:26:1117 days ago1718105171
Sandclock Yield ETH: scWETHv2 Token
464.67067659 ETH
200682752024-06-11 11:26:1117 days ago1718105171
Sandclock Yield ETH: scWETHv2 Token
464.67067659 ETH
200668242024-06-11 6:34:1118 days ago1718087651
Sandclock Yield ETH: scWETHv2 Token
40 ETH
200408622024-06-07 15:31:4721 days ago1717774307
Sandclock Yield ETH: scWETHv2 Token
3.9 ETH
200083092024-06-03 2:27:3526 days ago1717381655
Sandclock Yield ETH: scWETHv2 Token
10.10311937 ETH
200083092024-06-03 2:27:3526 days ago1717381655
Sandclock Yield ETH: scWETHv2 Token
10.10311937 ETH
200029422024-06-02 8:28:1127 days ago1717316891
Sandclock Yield ETH: scWETHv2 Token
29.72881257 ETH
200029422024-06-02 8:28:1127 days ago1717316891
Sandclock Yield ETH: scWETHv2 Token
29.72881257 ETH
199823762024-05-30 11:30:3529 days ago1717068635
Sandclock Yield ETH: scWETHv2 Token
19.43897177 ETH
199823762024-05-30 11:30:3529 days ago1717068635
Sandclock Yield ETH: scWETHv2 Token
19.43897177 ETH
199394392024-05-24 11:27:2335 days ago1716550043
Sandclock Yield ETH: scWETHv2 Token
11 ETH
199324732024-05-23 12:05:5936 days ago1716465959
Sandclock Yield ETH: scWETHv2 Token
50.96228605 ETH
199324732024-05-23 12:05:5936 days ago1716465959
Sandclock Yield ETH: scWETHv2 Token
50.96228605 ETH
199226102024-05-22 2:59:5938 days ago1716346799
Sandclock Yield ETH: scWETHv2 Token
0.65 ETH
199225302024-05-22 2:43:5938 days ago1716345839
Sandclock Yield ETH: scWETHv2 Token
0.2 ETH
198887162024-05-17 9:11:4743 days ago1715937107
Sandclock Yield ETH: scWETHv2 Token
140.20624702 ETH
198887162024-05-17 9:11:4743 days ago1715937107
Sandclock Yield ETH: scWETHv2 Token
140.20624702 ETH
198829672024-05-16 13:54:5943 days ago1715867699
Sandclock Yield ETH: scWETHv2 Token
1.5 ETH
198764322024-05-15 15:59:4744 days ago1715788787
Sandclock Yield ETH: scWETHv2 Token
549.40062531 ETH
198764322024-05-15 15:59:4744 days ago1715788787
Sandclock Yield ETH: scWETHv2 Token
549.40062531 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
scWETHv2

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
paris EvmVersion
File 1 of 31 : scWETHv2.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.13;

import {
    ZeroAddress,
    InvalidSlippageTolerance,
    InsufficientDepositBalance,
    FloatBalanceTooLow
} from "../errors/scErrors.sol";

import {ERC20} from "solmate/tokens/ERC20.sol";
import {WETH} from "solmate/tokens/WETH.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
import {Address} from "openzeppelin-contracts/utils/Address.sol";
import {EnumerableMap} from "openzeppelin-contracts/utils/structs/EnumerableMap.sol";

import {Constants as C} from "../lib/Constants.sol";
import {BaseV2Vault} from "./BaseV2Vault.sol";
import {IAdapter} from "./IAdapter.sol";
import {IwstETH} from "../interfaces/lido/IwstETH.sol";
import {PriceConverter} from "./PriceConverter.sol";
import {Swapper} from "./Swapper.sol";

/**
 * @title Sandclock WETH Vault version 2
 * @notice Deposit Asset : Weth or Eth
 * This vault leverages the supplied weth using flashloans, stakes the leveraged eth, supplies the wstEth as collateral
 * and subesequently borrows weth on that collateral to payback the flashloan
 * The bulk of the interest is earned from staking eth
 * In contrast to scWETHv1 which used only one pre coded lending market
 * scWETHv2 can use multiple lending markets, which can be controlled by adding or removing adapter contracts into the vault
 */
contract scWETHv2 is BaseV2Vault {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;
    using Address for address;
    using EnumerableMap for EnumerableMap.UintToAddressMap;

    event Harvested(uint256 profitSinceLastHarvest, uint256 performanceFee);
    event MinFloatAmountUpdated(address indexed user, uint256 newMinFloatAmount);
    event Rebalanced(uint256 totalCollateral, uint256 totalDebt, uint256 floatBalance);
    event SuppliedAndBorrowed(uint256 adapterId, uint256 supplyAmount, uint256 borrowAmount);
    event RepaidAndWithdrawn(uint256 adapterId, uint256 repayAmount, uint256 withdrawAmount);
    event WithdrawnToVault(uint256 amount);

    // total invested during last harvest/rebalance
    uint256 public totalInvested;

    // total profit generated for this vault
    uint256 public totalProfit;

    // since the totalAssets increases after profit, the floatRequired also increases proportionally in case of using a percentage float
    // this will cause the receiveFlashloan method to fail on reinvesting profits (using rebalance) after the multicall, since the actual float in the contract remain unchanged after the multicall
    // this can be fixed by also withdrawing float into the contract in the reinvesting profits multicall but that makes the calculations very complex on the backend
    // a simple solution to that is just using minimumFloatAmount instead of a percentage float
    uint256 public minimumFloatAmount = 1 ether;

    IwstETH constant wstETH = IwstETH(C.WSTETH);

    constructor(address _admin, address _keeper, WETH _weth, Swapper _swapper, PriceConverter _priceConverter)
        BaseV2Vault(_admin, _keeper, _weth, _priceConverter, _swapper, "Sandclock WETH Vault v2", "scWETHv2")
    {
        zeroExSwapWhitelist[ERC20(C.WSTETH)] = true;
    }

    /*//////////////////////////////////////////////////////////////
                            PUBLIC API
    //////////////////////////////////////////////////////////////*/

    // need to be able to receive eth
    receive() external payable {}

    /// @notice set the minimum amount of weth that must be present in the vault
    /// @param _newMinFloatAmount the new minimum float amount
    function setMinimumFloatAmount(uint256 _newMinFloatAmount) external {
        _onlyAdmin();

        minimumFloatAmount = _newMinFloatAmount;

        emit MinFloatAmountUpdated(msg.sender, _newMinFloatAmount);
    }

    /// @notice the primary method to be used by backend to invest, disinvest or reallocate funds among supported adapters
    /// @dev _totalInvestAmount must be zero in case of disinvest, reallocation or reinvesting profits
    /// @dev also mints performance fee tokens to the treasury based on the profits (if any) made by the vault
    /// @param _totalInvestAmount total amount of float in the strategy to invest in the lending markets in case of a invest
    /// @param _flashLoanAmount the amount to be flashloaned from balancer
    /// @param _multicallData array of bytes containing the series of encoded functions to be called (the functions being one of supplyAndBorrow, repayAndWithdraw, swapWstEthToWeth, swapWethToWstEth, zeroExSwap)
    function rebalance(uint256 _totalInvestAmount, uint256 _flashLoanAmount, bytes[] calldata _multicallData)
        external
    {
        _onlyKeeper();

        if (_totalInvestAmount > asset.balanceOf(address(this))) revert InsufficientDepositBalance();

        // needed otherwise counted as profit during harvest
        totalInvested += _totalInvestAmount;

        _flashLoan(_flashLoanAmount, _multicallData);

        _harvest();

        emit Rebalanced(totalCollateral(), totalDebt(), asset.balanceOf(address(this)));
    }

    /// @notice swap weth to wstEth
    /// @dev the keeper will mostly use 0x (zeroExSwap method) for swapping weth to wstEth between rebalancing
    /// @dev this method is just a precaution and to be only used by the keeper in case zeroEx API goes down
    /// @param _wethAmount amount of weth to be swapped to wstEth
    function swapWethToWstEth(uint256 _wethAmount) external {
        _onlyKeeperOrFlashLoan();

        address(swapper).functionDelegateCall(
            abi.encodeWithSelector(Swapper.lidoSwapWethToWstEth.selector, _wethAmount)
        );
    }

    /// @notice swap wstEth to weth
    /// @dev mainly to be used in the multicall to swap withdrawn wstEth to weth to payback the flashloan
    /// @param _wstEthAmount amount of wstEth to be swapped to weth
    /// @param _slippageTolerance the max slippage during steth to eth swap (1e18 meaning 0 slippage tolerance)
    function swapWstEthToWeth(uint256 _wstEthAmount, uint256 _slippageTolerance) external {
        _onlyKeeperOrFlashLoan();

        if (_slippageTolerance > C.ONE) revert InvalidSlippageTolerance();

        uint256 wstEthBalance = wstETH.balanceOf(address(this));

        if (_wstEthAmount > wstEthBalance) {
            _wstEthAmount = wstEthBalance;
        }

        uint256 stEthAmount = wstETH.unwrap(_wstEthAmount);

        uint256 wethAmountOutMin = priceConverter.stEthToEth(stEthAmount).mulWadDown(_slippageTolerance);

        address(swapper).functionDelegateCall(
            abi.encodeWithSelector(Swapper.curveSwapStEthToWeth.selector, stEthAmount, wethAmountOutMin)
        );
    }

    /// @notice withdraw deposited funds from the lending markets to the vault
    /// @param _amount : amount of assets to withdraw to the vault
    function withdrawToVault(uint256 _amount) external {
        _onlyKeeper();

        _withdrawToVault(_amount);
    }

    /// @notice returns the adapter address given the adapterId (only if the adaapterId is supported else returns zero address)
    /// @param _adapterId the id of the adapter to check
    function getAdapter(uint256 _adapterId) external view returns (address adapter) {
        (, adapter) = protocolAdapters.tryGet(_adapterId);
    }

    /// @notice returns the total assets (in WETH) held by the strategy
    function totalAssets() public view override returns (uint256 assets) {
        // value of the supplied collateral in eth terms using chainlink oracle
        assets = _totalCollateralInWeth();

        // subtract the debt
        assets -= totalDebt();

        // add float
        assets += asset.balanceOf(address(this));
    }

    /// @notice returns the wstEth deposited of the vault in a particular protocol
    /// @param _adapterId The id the protocol adapter
    function getCollateral(uint256 _adapterId) public view returns (uint256) {
        if (!isSupported(_adapterId)) return 0;

        return IAdapter(protocolAdapters.get(_adapterId)).getCollateral(address(this));
    }

    /// @notice returns the total wstEth supplied as collateral
    function totalCollateral() public view returns (uint256 collateral) {
        uint256 n = protocolAdapters.length();
        address adapter;

        for (uint256 i; i < n; i++) {
            (, adapter) = protocolAdapters.at(i);
            collateral += IAdapter(adapter).getCollateral(address(this));
        }
    }

    /// @notice returns the weth debt of the vault in a particularly protocol
    /// @param _adapterId The id the protocol adapter
    function getDebt(uint256 _adapterId) public view returns (uint256) {
        if (!isSupported(_adapterId)) return 0;

        return IAdapter(protocolAdapters.get(_adapterId)).getDebt(address(this));
    }

    /// @notice returns the total WETH borrowed
    function totalDebt() public view returns (uint256 debt) {
        uint256 n = protocolAdapters.length();
        address adapter;

        for (uint256 i; i < n; i++) {
            (, adapter) = protocolAdapters.at(i);
            debt += IAdapter(adapter).getDebt(address(this));
        }
    }

    /// @notice helper method for the user to directly deposit ETH to this vault instead of weth
    /// @param receiver the address to mint the shares to
    function deposit(address receiver) external payable returns (uint256 shares) {
        uint256 assets = msg.value;

        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

        // wrap eth
        WETH(payable(address(asset))).deposit{value: assets}();

        _mint(receiver, shares);

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

        afterDeposit(assets, shares);
    }

    /// @dev called after the flashLoan on rebalance
    function receiveFlashLoan(
        address[] memory,
        uint256[] memory amounts,
        uint256[] memory feeAmounts,
        bytes memory userData
    ) external {
        _isFlashLoanInitiated();

        // decode user data
        bytes[] memory callData = abi.decode(userData, (bytes[]));

        _multiCall(callData);

        // payback flashloan
        asset.safeTransfer(address(balancerVault), amounts[0] + feeAmounts[0]);

        _enforceFloat();
    }

    /// @notice supplies wstEth as collateral and borrows weth from the respective protocol as specified by adapterId
    /// @dev mainly to be used inside the multicall to supply and borrow assets from the respective lending market
    /// @param _adapterId the id of the adapter for the required protocol
    /// @param _supplyAmount the amount of wstEth to be supplied as collateral
    /// @param _borrowAmount the amount of weth to be borrowed
    function supplyAndBorrow(uint256 _adapterId, uint256 _supplyAmount, uint256 _borrowAmount) external {
        _onlyKeeperOrFlashLoan();

        address adapter = protocolAdapters.get(_adapterId);

        _adapterDelegateCall(adapter, abi.encodeWithSelector(IAdapter.supply.selector, _supplyAmount));
        _adapterDelegateCall(adapter, abi.encodeWithSelector(IAdapter.borrow.selector, _borrowAmount));

        emit SuppliedAndBorrowed(_adapterId, _supplyAmount, _borrowAmount);
    }

    /// @notice repays weth debt and withdraws wstEth collateral from the respective protocol as specified by adapterId
    /// @dev mainly to be used inside the multicall to repay and withdraw assets from the respective lending market
    /// @param _adapterId the id of the adapter for the required protocol
    /// @param _repayAmount the amount of weth to be repaid
    /// @param _withdrawAmount the amount of wstEth to be withdrawn
    function repayAndWithdraw(uint256 _adapterId, uint256 _repayAmount, uint256 _withdrawAmount) external {
        _onlyKeeperOrFlashLoan();

        address adapter = protocolAdapters.get(_adapterId);

        _adapterDelegateCall(adapter, abi.encodeWithSelector(IAdapter.repay.selector, _repayAmount));
        _adapterDelegateCall(adapter, abi.encodeWithSelector(IAdapter.withdraw.selector, _withdrawAmount));

        emit RepaidAndWithdrawn(_adapterId, _repayAmount, _withdrawAmount);
    }

    function withdraw(uint256 assets, address receiver, address owner) public override returns (uint256 shares) {
        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

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

        // since during withdrawing everything,
        // actual withdrawn amount might be less than totalAsssets
        // (due to slippage incurred during wstEth to weth swap)
        if (assets > balance) {
            assets = balance;
        }

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

        asset.safeTransfer(receiver, assets);
    }

    function redeem(uint256 shares, address receiver, address owner) public override returns (uint256 assets) {
        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        // Check for rounding error since we round down in previewRedeem.
        require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

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

        // since during withdrawing everything,
        // actual withdrawn amount might be less than totalAsssets
        // (due to slippage incurred during wstEth to weth swap)
        if (assets > balance) {
            assets = balance;
        }

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

        asset.safeTransfer(receiver, assets);
    }

    /*//////////////////////////////////////////////////////////////
                            INTERNAL API
    //////////////////////////////////////////////////////////////*/

    function _withdrawToVault(uint256 _amount) internal {
        uint256 n = protocolAdapters.length();
        uint256 flashLoanAmount;
        uint256 totalInvested_ = _totalCollateralInWeth() - totalDebt();
        bytes[] memory callData = new bytes[](n + 1); // +1 for the last call to swap wstEth to weth

        // limit the amount to withdraw to the total invested amount
        if (_amount > totalInvested_) _amount = totalInvested_;

        uint256 id;
        address adapter;
        uint256 repayPerProtocol;
        uint256 withdrawPerProtocol;
        for (uint256 i; i < n; i++) {
            (id, adapter) = protocolAdapters.at(i);
            uint256 collateral = IAdapter(adapter).getCollateral(address(this));

            // skip if there is no position on this protocol
            if (collateral == 0) continue;

            uint256 debt = IAdapter(adapter).getDebt(address(this));
            uint256 assets = priceConverter.wstEthToEth(collateral) - debt;

            // withdraw from each protocol in equal weight (based on the relative allocation)
            withdrawPerProtocol = _amount.mulDivDown(assets, totalInvested_);
            repayPerProtocol = withdrawPerProtocol.mulDivDown(debt, assets);
            flashLoanAmount += repayPerProtocol;

            callData[i] = abi.encodeWithSelector(
                this.repayAndWithdraw.selector,
                id,
                repayPerProtocol,
                priceConverter.ethToWstEth(repayPerProtocol + withdrawPerProtocol)
            );
        }

        // needed otherwise counted as loss during harvest
        totalInvested -= _amount;

        callData[n] = abi.encodeWithSelector(scWETHv2.swapWstEthToWeth.selector, type(uint256).max, slippageTolerance);

        uint256 float = asset.balanceOf(address(this));

        _flashLoan(flashLoanAmount, callData);

        emit WithdrawnToVault(asset.balanceOf(address(this)) - float);
    }

    /// @notice reverts if float in the vault is not above the minimum required
    function _enforceFloat() internal view {
        uint256 float = asset.balanceOf(address(this));
        uint256 floatRequired = minimumFloatAmount;

        if (float < floatRequired) revert FloatBalanceTooLow(float, floatRequired);
    }

    function beforeWithdraw(uint256 assets, uint256) internal override {
        uint256 float = asset.balanceOf(address(this));

        if (assets <= float) return;

        uint256 missing = assets + minimumFloatAmount - float;

        _withdrawToVault(missing);
    }

    function _flashLoan(uint256 _totalFlashLoanAmount, bytes[] memory callData) internal {
        address[] memory tokens = new address[](1);
        tokens[0] = address(asset);
        uint256[] memory amounts = new uint256[](1);
        amounts[0] = _totalFlashLoanAmount;

        _initiateFlashLoan();
        balancerVault.flashLoan(address(this), tokens, amounts, abi.encode(callData));
        _finalizeFlashLoan();
    }

    function _harvest() internal {
        // store the old total
        uint256 oldTotalInvested = totalInvested;
        uint256 assets = _totalCollateralInWeth() - totalDebt();

        if (assets > oldTotalInvested) {
            totalInvested = assets;

            // profit since last harvest, zero if there was a loss
            uint256 profit = assets - oldTotalInvested;
            totalProfit += profit;

            uint256 fee = profit.mulWadDown(performanceFee);

            // mint equivalent amount of tokens to the performance fee beneficiary ie the treasury
            _mint(treasury, convertToShares(fee));

            emit Harvested(profit, fee);
        }
    }

    function _totalCollateralInWeth() internal view returns (uint256) {
        return priceConverter.wstEthToEth(totalCollateral());
    }
}

File 2 of 31 : scErrors.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.10;

error InvalidTargetLtv();
error InvalidMaxLtv();
error InvalidFlashLoanCaller();
error InvalidSlippageTolerance();
error InvalidFloatPercentage();
error ZeroAddress();
error PleaseUseRedeemMethod();
error FeesTooHigh();
error TreasuryCannotBeZero();
error VaultNotUnderwater();
error CallerNotAdmin();
error CallerNotKeeper();
error NoProfitsToSell();
error EndUsdcBalanceTooLow();
error InsufficientDepositBalance();
error AmountReceivedBelowMin();
error FlashLoanAmountZero();
error ProtocolNotSupported(uint256 adapterId);
error ProtocolInUse(uint256 adapterId);
error FloatBalanceTooLow(uint256 actual, uint256 required);
error TokenOutNotAllowed(address token);

File 3 of 31 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 4 of 31 : WETH.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

import {SafeTransferLib} from "../utils/SafeTransferLib.sol";

/// @notice Minimalist and modern Wrapped Ether implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol)
/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
contract WETH is ERC20("Wrapped Ether", "WETH", 18) {
    using SafeTransferLib for address;

    event Deposit(address indexed from, uint256 amount);

    event Withdrawal(address indexed to, uint256 amount);

    function deposit() public payable virtual {
        _mint(msg.sender, msg.value);

        emit Deposit(msg.sender, msg.value);
    }

    function withdraw(uint256 amount) public virtual {
        _burn(msg.sender, amount);

        emit Withdrawal(msg.sender, amount);

        msg.sender.safeTransferETH(amount);
    }

    receive() external payable virtual {
        deposit();
    }
}

File 5 of 31 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 6 of 31 : FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

File 7 of 31 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 8 of 31 : EnumerableMap.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.

pragma solidity ^0.8.0;

import "./EnumerableSet.sol";

/**
 * @dev Library for managing an enumerable variant of Solidity's
 * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
 * type.
 *
 * Maps have the following properties:
 *
 * - Entries are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Entries are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableMap for EnumerableMap.UintToAddressMap;
 *
 *     // Declare a set state variable
 *     EnumerableMap.UintToAddressMap private myMap;
 * }
 * ```
 *
 * The following map types are supported:
 *
 * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
 * - `address -> uint256` (`AddressToUintMap`) since v4.6.0
 * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
 * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
 * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableMap.
 * ====
 */
library EnumerableMap {
    using EnumerableSet for EnumerableSet.Bytes32Set;

    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Map type with
    // bytes32 keys and values.
    // The Map implementation uses private functions, and user-facing
    // implementations (such as Uint256ToAddressMap) are just wrappers around
    // the underlying Map.
    // This means that we can only create new EnumerableMaps for types that fit
    // in bytes32.

    struct Bytes32ToBytes32Map {
        // Storage of keys
        EnumerableSet.Bytes32Set _keys;
        mapping(bytes32 => bytes32) _values;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(
        Bytes32ToBytes32Map storage map,
        bytes32 key,
        bytes32 value
    ) internal returns (bool) {
        map._values[key] = value;
        return map._keys.add(key);
    }

    /**
     * @dev Removes a key-value pair from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
        delete map._values[key];
        return map._keys.remove(key);
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
        return map._keys.contains(key);
    }

    /**
     * @dev Returns the number of key-value pairs in the map. O(1).
     */
    function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
        return map._keys.length();
    }

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

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
        bytes32 value = map._values[key];
        if (value == bytes32(0)) {
            return (contains(map, key), bytes32(0));
        } else {
            return (true, value);
        }
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
        bytes32 value = map._values[key];
        require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key");
        return value;
    }

    /**
     * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
    function get(
        Bytes32ToBytes32Map storage map,
        bytes32 key,
        string memory errorMessage
    ) internal view returns (bytes32) {
        bytes32 value = map._values[key];
        require(value != 0 || contains(map, key), errorMessage);
        return value;
    }

    // UintToUintMap

    struct UintToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(
        UintToUintMap storage map,
        uint256 key,
        uint256 value
    ) internal returns (bool) {
        return set(map._inner, bytes32(key), bytes32(value));
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

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

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
        (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
        return (success, uint256(value));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(key)));
    }

    /**
     * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
    function get(
        UintToUintMap storage map,
        uint256 key,
        string memory errorMessage
    ) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(key), errorMessage));
    }

    // UintToAddressMap

    struct UintToAddressMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(
        UintToAddressMap storage map,
        uint256 key,
        address value
    ) internal returns (bool) {
        return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToAddressMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

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

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
        (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
        return (success, address(uint160(uint256(value))));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
        return address(uint160(uint256(get(map._inner, bytes32(key)))));
    }

    /**
     * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
    function get(
        UintToAddressMap storage map,
        uint256 key,
        string memory errorMessage
    ) internal view returns (address) {
        return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage))));
    }

    // AddressToUintMap

    struct AddressToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(
        AddressToUintMap storage map,
        address key,
        uint256 value
    ) internal returns (bool) {
        return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(AddressToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

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

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
        (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
        return (success, uint256(value));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
    }

    /**
     * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
    function get(
        AddressToUintMap storage map,
        address key,
        string memory errorMessage
    ) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage));
    }

    // Bytes32ToUintMap

    struct Bytes32ToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(
        Bytes32ToUintMap storage map,
        bytes32 key,
        uint256 value
    ) internal returns (bool) {
        return set(map._inner, key, bytes32(value));
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

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

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
        (bool success, bytes32 value) = tryGet(map._inner, key);
        return (success, uint256(value));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
        return uint256(get(map._inner, key));
    }

    /**
     * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
    function get(
        Bytes32ToUintMap storage map,
        bytes32 key,
        string memory errorMessage
    ) internal view returns (uint256) {
        return uint256(get(map._inner, key, errorMessage));
    }
}

File 9 of 31 : Constants.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.13;

library Constants {
    // address of the multisig. meant to have default admin role
    address public constant MULTISIG = 0x035F210e5d14054E8AE5A6CFA76d643aA200D56E;

    uint256 public constant ONE = 1e18;
    // decimals difference between WETH and USDC (18 - 6)
    uint256 public constant WETH_USDC_DECIMALS_DIFF = 1e12;
    // value for the variable interest rate mode on Aave
    uint256 public constant AAVE_VAR_INTEREST_RATE_MODE = 2;
    // enable efficeincy mode on Aave (used to allow greater LTV when asset and debt tokens are correlated in price)
    uint8 public constant AAVE_EMODE_ID = 1;
    // vaule used to scale the token's collateral/borrow factors from the euler market
    uint32 constant EULER_CONFIG_FACTOR_SCALE = 4_000_000_000;

    /*//////////////////////////////////////////////////////////////
                          MAINNET ADDRESSES
    //////////////////////////////////////////////////////////////*/

    // address of the USDC token contract
    address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    // address of the WETH token contract
    address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    // address of the wrapped stETH token contract
    address public constant WSTETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;
    // address of the Lido stETH token contract
    address public constant STETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
    // address of the LUSD token contract
    address public constant LUSD = 0x5f98805A4E8be255a32880FDeC7F6728C6568bA0;

    // address of the Curve pool for ETH-stETH
    address public constant CURVE_ETH_STETH_POOL = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022;

    // address of the Uniswap v3 swap router contract
    address public constant UNISWAP_V3_SWAP_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;

    // address of the Aave v3 pool contract
    address public constant AAVE_V3_POOL = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2;
    // address of the Aave pool data provider contract
    address public constant AAVE_V3_POOL_DATA_PROVIDER = 0x7B4EB56E7CD4b454BA8ff71E4518426369a138a3;

    // address of the Aave v3 "aEthUSDC" token (supply token)
    address public constant AAVE_V3_AUSDC_TOKEN = 0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c;
    // address of the Aave v3 "aEthwstETH" token (supply token)
    address public constant AAVE_V3_AWSTETH_TOKEN = 0x0B925eD163218f6662a35e0f0371Ac234f9E9371;
    // address of the Aave v3 "variableDebtEthWETH" token (variable debt token)
    address public constant AAVE_V3_VAR_DEBT_WETH_TOKEN = 0xeA51d7853EEFb32b6ee06b1C12E6dcCA88Be0fFE;
    // address of the Aave v3 "variableDebtEthWETH" token implementation contract (variable debt token)
    address public constant AAVE_V3_VAR_DEBT_IMPLEMENTATION_CONTRACT = 0xaC725CB59D16C81061BDeA61041a8A5e73DA9EC6;

    // EULER Contracts
    address public constant EULER = 0x27182842E098f60e3D576794A5bFFb0777E025d3;
    address public constant EULER_MARKETS = 0x3520d5a913427E6F0D6A83E07ccD4A4da316e4d3;
    // Euler supply token for wstETH (ewstETH)
    address public constant EULER_ETOKEN_WSTETH = 0xbd1bd5C956684f7EB79DA40f582cbE1373A1D593;
    // Euler supply token for USDC (eUSDC)
    address public constant EULER_ETOKEN_USDC = 0xEb91861f8A4e1C12333F42DCE8fB0Ecdc28dA716;
    // Euler debt token weth
    address public constant EULER_DTOKEN_WETH = 0x62e28f054efc24b26A794F5C1249B6349454352C;
    // address of the EULER rewards token contract
    address public constant EULER_REWARDS_TOKEN = 0xd9Fcd98c322942075A5C3860693e9f4f03AAE07b;

    // adress of the Chainlink aggregator for the USDC/eth price feed
    address public constant CHAINLINK_USDC_ETH_PRICE_FEED = 0x986b5E1e1755e3C2440e960477f25201B0a8bbD4;
    // Chainlink pricefeed (stETH -> ETH)
    address public constant CHAINLINK_STETH_ETH_PRICE_FEED = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;
    // Liquity pricefeed (USD -> ETH) with Chainlink as primary and Tellor as backup.
    address public constant LIQUITY_USD_ETH_PRICE_FEED = 0x4c517D4e2C851CA76d7eC94B805269Df0f2201De;

    // address of the Balancer vault contract
    address public constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
    // Balancer admin account
    address public constant BALANCER_ADMIN = 0x97207B095e4D5C9a6e4cfbfcd2C3358E03B90c4A;
    // address of the Balance Protocol Fees Collector contract
    address public constant BALANCER_FEES_COLLECTOR = 0xce88686553686DA562CE7Cea497CE749DA109f9F;

    // address of the 0x swap router contract
    address public constant ZERO_EX_ROUTER = 0xDef1C0ded9bec7F1a1670819833240f027b25EfF;

    // Compound v3
    address public constant COMPOUND_V3_COMET_WETH = 0xA17581A9E3356d9A858b789D68B4d866e593aE94;

    // Aave v2 lending pool
    address public constant AAVE_V2_LENDING_POOL = 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9;
    // Aave v2 protocol data provider
    address public constant AAVE_V2_PROTOCOL_DATA_PROVIDER = 0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d;
    // Aave v2 interest bearing USDC (aUSDC) token
    address public constant AAVE_V2_AUSDC_TOKEN = 0xBcca60bB61934080951369a648Fb03DF4F96263C;
    // Aave v2 variable debt bearing WETH (variableDebtWETH) token
    address public constant AAVE_V2_VAR_DEBT_WETH_TOKEN = 0xF63B34710400CAd3e044cFfDcAb00a0f32E33eCf;

    // Liquity
    address public constant LIQUITY_STABILITY_POOL = 0x66017D22b0f8556afDd19FC67041899Eb65a21bb;
    address public constant LIQUITY_LQTY_TOKEN = 0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D;

    // Morpho
    address public constant MORPHO = 0x33333aea097c193e66081E930c33020272b33333;
}

File 10 of 31 : BaseV2Vault.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.19;

import {TokenOutNotAllowed} from "../errors/scErrors.sol";

import {ERC20} from "solmate/tokens/ERC20.sol";
import {Address} from "openzeppelin-contracts/utils/Address.sol";
import {EnumerableMap} from "openzeppelin-contracts/utils/structs/EnumerableMap.sol";

import {ProtocolNotSupported, ProtocolInUse, ZeroAddress} from "../errors/scErrors.sol";
import {Constants as C} from "../lib/Constants.sol";
import {IVault} from "../interfaces/balancer/IVault.sol";
import {IFlashLoanRecipient} from "../interfaces/balancer/IFlashLoanRecipient.sol";
import {IAdapter} from "./IAdapter.sol";
import {PriceConverter} from "./PriceConverter.sol";
import {Swapper} from "./Swapper.sol";
import {sc4626} from "../sc4626.sol";

/**
 * @title BaseV2Vault
 * @notice Base vault contract for v2 vaults to that use multiple lending markets thru adapters.
 */
abstract contract BaseV2Vault is sc4626, IFlashLoanRecipient {
    using Address for address;
    using EnumerableMap for EnumerableMap.UintToAddressMap;

    event SwapperUpdated(address indexed admin, address newSwapper);
    event ProtocolAdapterAdded(address indexed admin, uint256 adapterId, address adapter);
    event ProtocolAdapterRemoved(address indexed admin, uint256 adapterId);
    event RewardsClaimed(uint256 adapterId);
    event TokenSwapped(address token, uint256 amount, uint256 amountReceived);
    event TokenWhitelisted(address token, bool value);

    // Balancer vault for flashloans
    IVault public constant balancerVault = IVault(C.BALANCER_VAULT);

    // price converter contract
    PriceConverter public immutable priceConverter;

    // swapper contract for facilitating token swaps
    Swapper public swapper;

    // mapping of IDs to lending protocol adapter contracts
    EnumerableMap.UintToAddressMap internal protocolAdapters;

    // mapping for the tokenOuts allowed during zeroExSwap
    mapping(ERC20 => bool) internal zeroExSwapWhitelist;

    constructor(
        address _admin,
        address _keeper,
        ERC20 _asset,
        PriceConverter _priceConverter,
        Swapper _swapper,
        string memory _name,
        string memory _symbol
    ) sc4626(_admin, _keeper, _asset, _name, _symbol) {
        _zeroAddressCheck(address(_priceConverter));
        _zeroAddressCheck(address(_swapper));

        priceConverter = _priceConverter;
        swapper = _swapper;

        zeroExSwapWhitelist[_asset] = true;
    }

    /**
     * @notice whitelist (or cancel whitelist) a token to be swapped out using zeroExSwap
     * @param _token The token to whitelist
     * @param _value whether to whitelist or cancel whitelist
     */
    function whiteListOutToken(ERC20 _token, bool _value) external {
        _onlyAdmin();

        if (address(_token) == address(0)) revert ZeroAddress();

        zeroExSwapWhitelist[_token] = _value;

        emit TokenWhitelisted(address(_token), _value);
    }

    /**
     * @notice Set the swapper contract used for executing token swaps.
     * @param _newSwapper The new swapper contract.
     */
    function setSwapper(Swapper _newSwapper) external {
        _onlyAdmin();

        if (address(_newSwapper) == address(0)) revert ZeroAddress();

        swapper = _newSwapper;

        emit SwapperUpdated(msg.sender, address(_newSwapper));
    }

    /**
     * @notice Add a new protocol adapter to the vault.
     * @param _adapter The adapter to add.
     */
    function addAdapter(IAdapter _adapter) external {
        _onlyAdmin();

        uint256 id = _adapter.id();

        if (isSupported(id)) revert ProtocolInUse(id);

        protocolAdapters.set(id, address(_adapter));

        address(_adapter).functionDelegateCall(abi.encodeWithSelector(IAdapter.setApprovals.selector));

        emit ProtocolAdapterAdded(msg.sender, id, address(_adapter));
    }

    /**
     * @notice Remove a protocol adapter from the vault. Reverts if the adapter is in use unless _force is true.
     * @param _adapterId The ID of the adapter to remove.
     * @param _force Whether or not to force the removal of the adapter.
     */
    function removeAdapter(uint256 _adapterId, bool _force) external {
        _onlyAdmin();
        _isSupportedCheck(_adapterId);

        // check if protocol is being used
        if (!_force && IAdapter(protocolAdapters.get(_adapterId)).getCollateral(address(this)) > 0) {
            revert ProtocolInUse(_adapterId);
        }

        _adapterDelegateCall(_adapterId, abi.encodeWithSelector(IAdapter.revokeApprovals.selector));

        protocolAdapters.remove(_adapterId);

        emit ProtocolAdapterRemoved(msg.sender, _adapterId);
    }

    /**
     * @notice Check if a lending market adapter is supported/used.
     * @param _adapterId The ID of the lending market adapter.
     */
    function isSupported(uint256 _adapterId) public view returns (bool) {
        return protocolAdapters.contains(_adapterId);
    }

    /**
     * @notice returns whether a token is whitelisted to be swapped out using zeroExSwap or not
     */
    function isTokenWhitelisted(ERC20 _token) external view returns (bool) {
        return zeroExSwapWhitelist[_token];
    }

    /**
     * @notice Claim rewards from a lending market.
     * @param _adapterId The ID of the lending market adapter.
     * @param _callData The encoded data for the claimRewards function.
     */
    function claimRewards(uint256 _adapterId, bytes calldata _callData) external {
        _onlyKeeperOrFlashLoan();
        _isSupportedCheck(_adapterId);

        _adapterDelegateCall(_adapterId, abi.encodeWithSelector(IAdapter.claimRewards.selector, _callData));

        emit RewardsClaimed(_adapterId);
    }

    /**
     * @notice Sell any token for the "asset" token on 0x exchange.
     * @param _tokenIn The token to sell.
     * @param _tokenOut The token to buy.
     * @param _amount The amount of tokens to sell.
     * @param _swapData The swap data for 0xrouter.
     * @param _assetAmountOutMin The minimum amount of "asset" token to receive for the swap.
     */
    function zeroExSwap(
        ERC20 _tokenIn,
        ERC20 _tokenOut,
        uint256 _amount,
        bytes calldata _swapData,
        uint256 _assetAmountOutMin
    ) external {
        _onlyKeeperOrFlashLoan();

        if (!zeroExSwapWhitelist[_tokenOut]) revert TokenOutNotAllowed(address(_tokenOut));

        bytes memory result = address(swapper).functionDelegateCall(
            abi.encodeWithSelector(
                Swapper.zeroExSwap.selector, _tokenIn, _tokenOut, _amount, _assetAmountOutMin, _swapData
            )
        );

        emit TokenSwapped(address(_tokenIn), _amount, abi.decode(result, (uint256)));
    }

    function _multiCall(bytes[] memory _callData) internal {
        for (uint256 i = 0; i < _callData.length; i++) {
            if (_callData[i].length == 0) continue;

            address(this).functionDelegateCall(_callData[i]);
        }
    }

    function _adapterDelegateCall(uint256 _adapterId, bytes memory _data) internal {
        protocolAdapters.get(_adapterId).functionDelegateCall(_data);
    }

    function _adapterDelegateCall(address _adapter, bytes memory _data) internal {
        _adapter.functionDelegateCall(_data);
    }

    function _isSupportedCheck(uint256 _adapterId) internal view {
        if (!isSupported(_adapterId)) revert ProtocolNotSupported(_adapterId);
    }

    function _zeroAddressCheck(address _address) internal pure {
        if (_address == address(0)) revert ZeroAddress();
    }
}

File 11 of 31 : IAdapter.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.19;

/**
 * @notice Interface for adapters that allow interactions with the lending protocols
 */
interface IAdapter {
    /**
     * @notice Returns the adapter's ID
     */
    function id() external view returns (uint256);

    /**
     * @notice Sets the necessary approvals (allowances) for interacting with the lending protocol
     */
    function setApprovals() external;

    /**
     * @notice Removes the given approvals (allowances) for interacting with the lending protocol
     */
    function revokeApprovals() external;

    /**
     * @notice Supplies the given amount of collateral to the lending protocol
     * @param amount The amount of collateral to supply
     */
    function supply(uint256 amount) external;

    /**
     * @notice Borrows the given amount of debt from the lending protocol
     * @param amount The amount of debt to borrow
     */
    function borrow(uint256 amount) external;

    /**
     * @notice Repays the given amount of debt to the lending protocol
     * @param amount The amount of debt to repay
     */
    function repay(uint256 amount) external;

    /**
     * @notice Withdraws the given amount of collateral from the lending protocol
     * @param amount The amount of collateral to withdraw
     */
    function withdraw(uint256 amount) external;

    /**
     * @notice Claims rewards awarded by the lending protocol
     * @param data Any data needed for the claim process
     */
    function claimRewards(bytes calldata data) external;

    /**
     * @notice Returns the amount of collateral currently supplied to the lending protocol
     * @param account The account to check
     */
    function getCollateral(address account) external view returns (uint256);

    /**
     * @notice Returns the amount of debt currently borrowed from the lending protocol
     * @param account The account to check
     */
    function getDebt(address account) external view returns (uint256);

    /**
     * @notice Returns the maximum loan-to-value (LTV) ratio for the lending protocol
     */
    function getMaxLtv() external view returns (uint256);
}

File 12 of 31 : IwstETH.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.10;

import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";

interface IwstETH is IERC20 {
    function wrap(uint256 _stETHAmount) external returns (uint256);

    /**
     * @notice Exchanges wstETH to stETH
     * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH
     * @dev Requirements:
     *  - `_wstETHAmount` must be non-zero
     *  - msg.sender must have at least `_wstETHAmount` wstETH.
     * @return Amount of stETH user receives after unwrap
     */
    function unwrap(uint256 _wstETHAmount) external returns (uint256);

    /**
     * @notice Get amount of wstETH for a given amount of stETH
     * @param _stETHAmount amount of stETH
     * @return Amount of wstETH for a given stETH amount
     */
    function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256);

    /**
     * @notice Get amount of stETH for a given amount of wstETH
     * @param _wstETHAmount amount of wstETH
     * @return Amount of stETH for a given wstETH amount
     */
    function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256);

    /**
     * @notice Get amount of stETH for a one wstETH
     * @return Amount of stETH for 1 wstETH
     */
    function stEthPerToken() external view returns (uint256);

    /**
     * @notice Get amount of wstETH for a one stETH
     * @return Amount of wstETH for a 1 stETH
     */
    function tokensPerStEth() external view returns (uint256);
}

File 13 of 31 : PriceConverter.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.19;

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

import {ZeroAddress, CallerNotAdmin} from "../errors/scErrors.sol";
import {Constants as C} from "../lib/Constants.sol";
import {AggregatorV3Interface} from "../interfaces/chainlink/AggregatorV3Interface.sol";
import {IwstETH} from "../interfaces/lido/IwstETH.sol";

/**
 * @title Price Converter
 * @notice Contract for price conversion between assets used by staking vaults.
 */
contract PriceConverter is AccessControl {
    using FixedPointMathLib for uint256;

    IwstETH constant wstETH = IwstETH(C.WSTETH);

    event UsdcToEthPriceFeedUpdated(address indexed admin, address newPriceFeed);
    event StEthToEthPriceFeedUpdated(address indexed admin, address newPriceFeed);

    // Chainlink price feed (USDC -> ETH)
    AggregatorV3Interface public usdcToEthPriceFeed = AggregatorV3Interface(C.CHAINLINK_USDC_ETH_PRICE_FEED);

    // Chainlink price feed (stETH -> ETH)
    AggregatorV3Interface public stEThToEthPriceFeed = AggregatorV3Interface(C.CHAINLINK_STETH_ETH_PRICE_FEED);

    constructor(address _admin) {
        _zeroAddressCheck(_admin);
        _grantRole(DEFAULT_ADMIN_ROLE, _admin);
    }

    function _onlyAdmin() internal view {
        if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert CallerNotAdmin();
    }

    /**
     * @notice Set the chainlink price feed for USDC -> WETH.
     * @param _newPriceFeed The new price feed.
     */
    function setUsdcToEthPriceFeed(address _newPriceFeed) external {
        _onlyAdmin();
        _zeroAddressCheck(_newPriceFeed);

        usdcToEthPriceFeed = AggregatorV3Interface(_newPriceFeed);

        emit UsdcToEthPriceFeedUpdated(msg.sender, address(_newPriceFeed));
    }

    /// @notice Set the chainlink price feed for stETH -> ETH.
    /// @param _newPriceFeed The new price feed.
    function setStEThToEthPriceFeed(address _newPriceFeed) external {
        _onlyAdmin();
        _zeroAddressCheck(_newPriceFeed);

        stEThToEthPriceFeed = AggregatorV3Interface(_newPriceFeed);

        emit StEthToEthPriceFeedUpdated(msg.sender, address(_newPriceFeed));
    }

    /**
     * @notice Returns the USDC fair value for the ETH amount provided.
     * @param _ethAmount The amount of ETH.
     */
    function ethToUsdc(uint256 _ethAmount) public view returns (uint256) {
        (, int256 usdcPriceInEth,,,) = usdcToEthPriceFeed.latestRoundData();

        return _ethAmount.divWadDown(uint256(usdcPriceInEth) * C.WETH_USDC_DECIMALS_DIFF);
    }

    /**
     * @notice Returns the ETH fair value for the USDC amount provided.
     * @param _usdcAmount The amount of USDC.
     */
    function usdcToEth(uint256 _usdcAmount) public view returns (uint256) {
        (, int256 usdcPriceInEth,,,) = usdcToEthPriceFeed.latestRoundData();

        return (_usdcAmount * C.WETH_USDC_DECIMALS_DIFF).mulWadDown(uint256(usdcPriceInEth));
    }

    function ethToWstEth(uint256 ethAmount) public view returns (uint256) {
        (, int256 price,,,) = stEThToEthPriceFeed.latestRoundData();

        uint256 stEthAmount = ethAmount.divWadDown(uint256(price));

        return wstETH.getWstETHByStETH(stEthAmount);
    }

    function stEthToEth(uint256 _stEthAmount) public view returns (uint256) {
        (, int256 price,,,) = stEThToEthPriceFeed.latestRoundData();

        return _stEthAmount.mulWadDown(uint256(price));
    }

    function wstEthToEth(uint256 wstEthAmount) public view returns (uint256) {
        // wstETh to stEth using exchangeRate
        uint256 stEthAmount = wstETH.getStETHByWstETH(wstEthAmount);

        return stEthToEth(stEthAmount);
    }

    function _zeroAddressCheck(address _address) internal pure {
        if (_address == address(0)) revert ZeroAddress();
    }
}

File 14 of 31 : Swapper.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.19;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {WETH} from "solmate/tokens/WETH.sol";
import {ILido} from "../interfaces/lido/ILido.sol";
import {IwstETH} from "../interfaces/lido/IwstETH.sol";
import {ICurvePool} from "../interfaces/curve/ICurvePool.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {Address} from "openzeppelin-contracts/utils/Address.sol";

import {AmountReceivedBelowMin} from "../errors/scErrors.sol";
import {ISwapRouter} from "../interfaces/uniswap/ISwapRouter.sol";
import {Constants as C} from "../lib/Constants.sol";

/**
 * @title Swapper
 * @notice Contract facilitating token swaps on Uniswap V3 and 0x.
 * @dev This contract is only meant to be used via delegatecalls from another contract.
 * @dev Using this contract directly for swaps might result in reverts.
 */
contract Swapper {
    using SafeTransferLib for ERC20;
    using Address for address;

    // Uniswap V3 router
    ISwapRouter public constant swapRouter = ISwapRouter(C.UNISWAP_V3_SWAP_ROUTER);

    ICurvePool public constant curvePool = ICurvePool(C.CURVE_ETH_STETH_POOL);

    WETH public constant weth = WETH(payable(C.WETH));
    ILido public constant stEth = ILido(C.STETH);
    IwstETH public constant wstETH = IwstETH(C.WSTETH);

    /**
     * @notice Swap tokens on Uniswap V3 using exact input single function.
     * @param _tokenIn Address of the token to swap.
     * @param _tokenOut Address of the token to receive.
     * @param _amountIn Amount of the token to swap.
     * @param _amountOutMin Minimum amount of the token to receive.
     */
    function uniswapSwapExactInput(
        ERC20 _tokenIn,
        ERC20 _tokenOut,
        uint256 _amountIn,
        uint256 _amountOutMin,
        uint24 _poolFee
    ) external returns (uint256) {
        ERC20(_tokenIn).safeApprove(address(swapRouter), _amountIn);

        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: address(_tokenIn),
            tokenOut: address(_tokenOut),
            fee: _poolFee,
            recipient: address(this),
            deadline: block.timestamp,
            amountIn: _amountIn,
            amountOutMinimum: _amountOutMin,
            sqrtPriceLimitX96: 0
        });

        return swapRouter.exactInputSingle(params);
    }

    /**
     * @notice Swap tokens on Uniswap V3 using exact output single function.
     * @param _tokenIn Address of the token to swap.
     * @param _tokenOut Address of the token to receive.
     * @param _amountOut Amount of the token to receive.
     * @param _amountInMaximum Maximum amount of the token to swap.
     */
    function uniswapSwapExactOutput(
        ERC20 _tokenIn,
        ERC20 _tokenOut,
        uint256 _amountOut,
        uint256 _amountInMaximum,
        uint24 _poolFee
    ) external returns (uint256) {
        ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams({
            tokenIn: address(_tokenIn),
            tokenOut: address(_tokenOut),
            fee: _poolFee,
            recipient: address(this),
            deadline: block.timestamp,
            amountOut: _amountOut,
            amountInMaximum: _amountInMaximum,
            sqrtPriceLimitX96: 0
        });

        _tokenIn.safeApprove(address(swapRouter), _amountInMaximum);

        uint256 amountIn = swapRouter.exactOutputSingle(params);

        _tokenIn.safeApprove(address(swapRouter), 0);

        return amountIn;
    }

    /**
     * @notice Swap tokens on 0x protocol.
     * @param _tokenIn Address of the token to swap.
     * @param _tokenOut Address of the token to receive.
     * @param _amountIn Amount of the token to swap.
     * @param _amountOutMin Minimum amount of the token to receive.
     * @param _swapData Encoded swap data obtained from 0x API.
     */
    function zeroExSwap(
        ERC20 _tokenIn,
        ERC20 _tokenOut,
        uint256 _amountIn,
        uint256 _amountOutMin,
        bytes calldata _swapData
    ) external returns (uint256) {
        uint256 tokenOutInitialBalance = _tokenOut.balanceOf(address(this));

        _tokenIn.safeApprove(C.ZERO_EX_ROUTER, _amountIn);

        C.ZERO_EX_ROUTER.functionCall(_swapData);

        uint256 amountReceived = _tokenOut.balanceOf(address(this)) - tokenOutInitialBalance;

        if (amountReceived < _amountOutMin) revert AmountReceivedBelowMin();

        _tokenIn.approve(C.ZERO_EX_ROUTER, 0);

        return amountReceived;
    }

    function lidoSwapWethToWstEth(uint256 _wethAmount) external {
        // weth to eth
        weth.withdraw(_wethAmount);

        // stake to lido / eth => stETH
        stEth.submit{value: _wethAmount}(address(0x00));

        //  stETH to wstEth
        uint256 stEthBalance = stEth.balanceOf(address(this));
        ERC20(address(stEth)).safeApprove(address(wstETH), stEthBalance);

        wstETH.wrap(stEthBalance);
    }

    function curveSwapStEthToWeth(uint256 _stEthAmount, uint256 _wethAmountOutMin)
        external
        returns (uint256 wethReceived)
    {
        // stETH to eth
        ERC20(address(stEth)).safeApprove(address(curvePool), _stEthAmount);

        wethReceived = curvePool.exchange(1, 0, _stEthAmount, _wethAmountOutMin);

        // eth to weth
        weth.deposit{value: address(this).balance}();
    }
}

File 15 of 31 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

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

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

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

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

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

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

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

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

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

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

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

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

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

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 16 of 31 : IVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IVault {
    /**
     * @dev Performs a 'flash loan', sending tokens to `recipient`, executing the `receiveFlashLoan` hook on it,
     * and then reverting unless the tokens plus a proportional protocol fee have been returned.
     *
     * The `tokens` and `amounts` arrays must have the same length, and each entry in these indicates the loan amount
     * for each token contract. `tokens` must be sorted in ascending order.
     *
     * The 'userData' field is ignored by the Vault, and forwarded as-is to `recipient` as part of the
     * `receiveFlashLoan` call.
     *
     * Emits `FlashLoan` events.
     */
    function flashLoan(address recipient, address[] memory tokens, uint256[] memory amounts, bytes memory userData)
        external;
}

File 17 of 31 : IFlashLoanRecipient.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Inspired by Aave Protocol's IFlashLoanReceiver.

interface IFlashLoanRecipient {
    /**
     * @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient.
     *
     * At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this
     * call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the
     * Vault, or else the entire flash loan will revert.
     *
     * `userData` is the same value passed in the `IVault.flashLoan` call.
     */
    function receiveFlashLoan(
        address[] memory tokens,
        uint256[] memory amounts,
        uint256[] memory feeAmounts,
        bytes memory userData
    ) external;
}

File 18 of 31 : sc4626.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.10;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {ERC4626} from "solmate/mixins/ERC4626.sol";
import {AccessControl} from "openzeppelin-contracts/access/AccessControl.sol";
import {Constants as C} from "./lib/Constants.sol";
import {
    CallerNotAdmin,
    CallerNotKeeper,
    ZeroAddress,
    InvalidFlashLoanCaller,
    TreasuryCannotBeZero,
    FeesTooHigh,
    InvalidFloatPercentage,
    InvalidSlippageTolerance
} from "./errors/scErrors.sol";

abstract contract sc4626 is ERC4626, AccessControl {
    constructor(address _admin, address _keeper, ERC20 _asset, string memory _name, string memory _symbol)
        ERC4626(_asset, _name, _symbol)
    {
        if (_admin == address(0)) revert ZeroAddress();
        if (_keeper == address(0)) revert ZeroAddress();

        _grantRole(DEFAULT_ADMIN_ROLE, _admin);
        _grantRole(KEEPER_ROLE, _keeper);
    }

    event TreasuryUpdated(address indexed user, address newTreasury);
    event PerformanceFeeUpdated(address indexed user, uint256 newPerformanceFee);
    event FloatPercentageUpdated(address indexed user, uint256 newFloatPercentage);
    event SlippageToleranceUpdated(address indexed admin, uint256 newSlippageTolerance);

    /// Role allowed to harvest/reinvest
    bytes32 public constant KEEPER_ROLE = keccak256("KEEPER_ROLE");

    // flag for checking flash loan caller
    bool public flashLoanInitiated;

    // address of the treasury to send performance fees to
    address public treasury;

    // performance fee percentage
    uint256 public performanceFee = 0.1e18; // 10%

    // percentage of the total assets to be kept in the vault as a withdrawal buffer
    uint256 public floatPercentage = 0.01e18;

    // max slippage tolerance for swaps
    uint256 public slippageTolerance = 0.99e18; // 1% default

    /// @notice set the treasury address
    /// @param _newTreasury the new treasury address
    function setTreasury(address _newTreasury) external {
        _onlyAdmin();

        if (_newTreasury == address(0)) revert TreasuryCannotBeZero();
        treasury = _newTreasury;
        emit TreasuryUpdated(msg.sender, _newTreasury);
    }

    /// @notice set the performance fee percentage
    /// @param _newPerformanceFee the new performance fee percentage
    /// @dev performance fee is a number between 0 and 1e18
    function setPerformanceFee(uint256 _newPerformanceFee) external {
        _onlyAdmin();

        if (_newPerformanceFee > 1e18) revert FeesTooHigh();
        performanceFee = _newPerformanceFee;
        emit PerformanceFeeUpdated(msg.sender, _newPerformanceFee);
    }

    /**
     * @notice Set the percentage of the total assets to be kept in the vault as a withdrawal buffer.
     * @param _newFloatPercentage The new float percentage value.
     */
    function setFloatPercentage(uint256 _newFloatPercentage) external {
        _onlyAdmin();

        if (_newFloatPercentage > C.ONE) revert InvalidFloatPercentage();

        floatPercentage = _newFloatPercentage;
        emit FloatPercentageUpdated(msg.sender, _newFloatPercentage);
    }

    /**
     * @notice Set the default slippage tolerance for swapping tokens.
     * @param _newSlippageTolerance The new slippage tolerance value.
     */
    function setSlippageTolerance(uint256 _newSlippageTolerance) external {
        _onlyAdmin();

        if (_newSlippageTolerance > C.ONE) revert InvalidSlippageTolerance();

        slippageTolerance = _newSlippageTolerance;

        emit SlippageToleranceUpdated(msg.sender, _newSlippageTolerance);
    }

    function _onlyAdmin() internal view {
        if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert CallerNotAdmin();
    }

    function _onlyKeeper() internal view {
        if (!hasRole(KEEPER_ROLE, msg.sender)) revert CallerNotKeeper();
    }

    function _onlyKeeperOrFlashLoan() internal view {
        if (!flashLoanInitiated) _onlyKeeper();
    }

    function _initiateFlashLoan() internal {
        flashLoanInitiated = true;
    }

    function _finalizeFlashLoan() internal {
        flashLoanInitiated = false;
    }

    function _isFlashLoanInitiated() internal view {
        if (!flashLoanInitiated) revert InvalidFlashLoanCaller();
    }
}

File 19 of 31 : 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 20 of 31 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

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

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

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

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

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

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

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

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

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

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

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

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

        _revokeRole(role, account);
    }

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

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

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

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

File 21 of 31 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

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

    function version() external view returns (uint256);

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

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

File 22 of 31 : ILido.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.10;

import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";

interface ILido is IERC20 {
    function submit(address _referral) external payable returns (uint256);

    /**
     * @return the entire amount of Ether controlled by the protocol.
     *
     * @dev The sum of all ETH balances in the protocol, equals to the total supply of stETH.
     */
    function getTotalPooledEther() external view returns (uint256);
}

File 23 of 31 : ICurvePool.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface ICurvePool {
    function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable returns (uint256);
}

File 24 of 31 : ISwapRouter.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.13;

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

    struct ExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
}

File 25 of 31 : ERC4626.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol";

/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)
abstract contract ERC4626 is ERC20 {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

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

    /*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    ERC20 public immutable asset;

    constructor(
        ERC20 _asset,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol, _asset.decimals()) {
        asset = _asset;
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

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

        afterDeposit(assets, shares);
    }

    function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

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

        afterDeposit(assets, shares);
    }

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual returns (uint256 shares) {
        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

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

        asset.safeTransfer(receiver, assets);
    }

    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual returns (uint256 assets) {
        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        // Check for rounding error since we round down in previewRedeem.
        require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

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

        asset.safeTransfer(receiver, assets);
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    function totalAssets() public view virtual returns (uint256);

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
    }

    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return convertToShares(assets);
    }

    function previewMint(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return convertToAssets(shares);
    }

    /*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return convertToAssets(balanceOf[owner]);
    }

    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HOOKS LOGIC
    //////////////////////////////////////////////////////////////*/

    function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}

    function afterDeposit(uint256 assets, uint256 shares) internal virtual {}
}

File 26 of 31 : 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 27 of 31 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 28 of 31 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

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

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

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

pragma solidity ^0.8.0;

import "./IERC165.sol";

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

File 30 of 31 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

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

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

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

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

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

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

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

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

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

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

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

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

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

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

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 31 of 31 : 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);
}

Settings
{
  "remappings": [
    "create3-factory/=lib/create3-factory/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "solmate/=lib/solmate/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
    "aave-v3/=lib/aave-v3-core/contracts/",
    "ERC4626/=lib/properties/lib/ERC4626/contracts/",
    "aave-v3-core/=lib/aave-v3-core/",
    "erc4626-tests/=lib/properties/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "euler-interfaces/=lib/euler-interfaces/contracts/",
    "properties/=lib/properties/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"contract WETH","name":"_weth","type":"address"},{"internalType":"contract Swapper","name":"_swapper","type":"address"},{"internalType":"contract PriceConverter","name":"_priceConverter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallerNotAdmin","type":"error"},{"inputs":[],"name":"CallerNotKeeper","type":"error"},{"inputs":[],"name":"FeesTooHigh","type":"error"},{"inputs":[{"internalType":"uint256","name":"actual","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"FloatBalanceTooLow","type":"error"},{"inputs":[],"name":"InsufficientDepositBalance","type":"error"},{"inputs":[],"name":"InvalidFlashLoanCaller","type":"error"},{"inputs":[],"name":"InvalidFloatPercentage","type":"error"},{"inputs":[],"name":"InvalidSlippageTolerance","type":"error"},{"inputs":[{"internalType":"uint256","name":"adapterId","type":"uint256"}],"name":"ProtocolInUse","type":"error"},{"inputs":[{"internalType":"uint256","name":"adapterId","type":"uint256"}],"name":"ProtocolNotSupported","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"TokenOutNotAllowed","type":"error"},{"inputs":[],"name":"TreasuryCannotBeZero","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"newFloatPercentage","type":"uint256"}],"name":"FloatPercentageUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"profitSinceLastHarvest","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"performanceFee","type":"uint256"}],"name":"Harvested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"newMinFloatAmount","type":"uint256"}],"name":"MinFloatAmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"newPerformanceFee","type":"uint256"}],"name":"PerformanceFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"},{"indexed":false,"internalType":"address","name":"adapter","type":"address"}],"name":"ProtocolAdapterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"}],"name":"ProtocolAdapterRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"floatBalance","type":"uint256"}],"name":"Rebalanced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"withdrawAmount","type":"uint256"}],"name":"RepaidAndWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"newSlippageTolerance","type":"uint256"}],"name":"SlippageToleranceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"supplyAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"SuppliedAndBorrowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"address","name":"newSwapper","type":"address"}],"name":"SwapperUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountReceived","type":"uint256"}],"name":"TokenSwapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"TokenWhitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"newTreasury","type":"address"}],"name":"TreasuryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawnToVault","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KEEPER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IAdapter","name":"_adapter","type":"address"}],"name":"addAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balancerVault","outputs":[{"internalType":"contract IVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"},{"internalType":"bytes","name":"_callData","type":"bytes"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"flashLoanInitiated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"floatPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"}],"name":"getAdapter","outputs":[{"internalType":"address","name":"adapter","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"}],"name":"getCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"}],"name":"getDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"}],"name":"isSupported","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"_token","type":"address"}],"name":"isTokenWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumFloatAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"performanceFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceConverter","outputs":[{"internalType":"contract PriceConverter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_totalInvestAmount","type":"uint256"},{"internalType":"uint256","name":"_flashLoanAmount","type":"uint256"},{"internalType":"bytes[]","name":"_multicallData","type":"bytes[]"}],"name":"rebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"feeAmounts","type":"uint256[]"},{"internalType":"bytes","name":"userData","type":"bytes"}],"name":"receiveFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"},{"internalType":"bool","name":"_force","type":"bool"}],"name":"removeAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"},{"internalType":"uint256","name":"_repayAmount","type":"uint256"},{"internalType":"uint256","name":"_withdrawAmount","type":"uint256"}],"name":"repayAndWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newFloatPercentage","type":"uint256"}],"name":"setFloatPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMinFloatAmount","type":"uint256"}],"name":"setMinimumFloatAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPerformanceFee","type":"uint256"}],"name":"setPerformanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newSlippageTolerance","type":"uint256"}],"name":"setSlippageTolerance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Swapper","name":"_newSwapper","type":"address"}],"name":"setSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newTreasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slippageTolerance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"},{"internalType":"uint256","name":"_supplyAmount","type":"uint256"},{"internalType":"uint256","name":"_borrowAmount","type":"uint256"}],"name":"supplyAndBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_wethAmount","type":"uint256"}],"name":"swapWethToWstEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_wstEthAmount","type":"uint256"},{"internalType":"uint256","name":"_slippageTolerance","type":"uint256"}],"name":"swapWstEthToWeth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapper","outputs":[{"internalType":"contract Swapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateral","outputs":[{"internalType":"uint256","name":"collateral","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebt","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalInvested","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalProfit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"_token","type":"address"},{"internalType":"bool","name":"_value","type":"bool"}],"name":"whiteListOutToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawToVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"_tokenIn","type":"address"},{"internalType":"contract ERC20","name":"_tokenOut","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_swapData","type":"bytes"},{"internalType":"uint256","name":"_assetAmountOutMin","type":"uint256"}],"name":"zeroExSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

61012060405267016345785d8a0000600855662386f26fc10000600955670dbd2fc137a30000600a55670de0b6b3a76400006012553480156200004157600080fd5b506040516200662f3803806200662f833981016040819052620000649162000432565b84848483856040518060400160405280601781526020017f53616e64636c6f636b2057455448205661756c742076320000000000000000008152506040518060400160405280600881526020016739b1aba2aa243b1960c11b81525086868684848282828181846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000109573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200012f9190620004b2565b60006200013d848262000583565b5060016200014c838262000583565b5060ff81166080524660a05262000162620002b0565b60c0525050506001600160a01b0392831660e05250508516620001985760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038416620001c05760405163d92e233d60e01b815260040160405180910390fd5b620001cd6000866200034c565b620001f97ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab856200034c565b50505050506200020f84620003f160201b60201c565b6200021a83620003f1565b50506001600160a01b0391821661010052600b80546001600160a01b031916918316919091179055166000908152600f60205260408120805460ff199081166001908117909255737f39c581f595b53c5cb19bd0b3f8da6c935e2ca09092527f0cefb75dc12b5e3095daa9383eb63e46bebe2c673f316abb0f117935b17bc538805490921617905550620006cd95505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620002e491906200064f565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60008281526006602090815260408083206001600160a01b038516845290915290205460ff16620003ed5760008281526006602090815260408083206001600160a01b03851684529091529020805460ff19166001179055620003ac3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6001600160a01b038116620004195760405163d92e233d60e01b815260040160405180910390fd5b50565b6001600160a01b03811681146200041957600080fd5b600080600080600060a086880312156200044b57600080fd5b855162000458816200041c565b60208701519095506200046b816200041c565b60408701519094506200047e816200041c565b606087015190935062000491816200041c565b6080870151909250620004a4816200041c565b809150509295509295909350565b600060208284031215620004c557600080fd5b815160ff81168114620004d757600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200050957607f821691505b6020821081036200052a57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200057e57600081815260208120601f850160051c81016020861015620005595750805b601f850160051c820191505b818110156200057a5782815560010162000565565b5050505b505050565b81516001600160401b038111156200059f576200059f620004de565b620005b781620005b08454620004f4565b8462000530565b602080601f831160018114620005ef5760008415620005d65750858301515b600019600386901b1c1916600185901b1785556200057a565b600085815260208120601f198616915b828110156200062057888601518255948401946001909101908401620005ff565b50858210156200063f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60008083546200065f81620004f4565b600182811680156200067a57600181146200069057620006c1565b60ff1984168752821515830287019450620006c1565b8760005260208060002060005b85811015620006b85781548a8201529084019082016200069d565b50505082870194505b50929695505050505050565b60805160a05160c05160e05161010051615e916200079e60003960008181610c2d0152818161235c015281816134d40152818161425101526143570152600081816107e301528181610f2d01528181611b5201528181611ce001528181611e2b01528181611eed0152818161270d0152818161280d015281816129ab01528181612aab015281816131960152818161331701528181613ba601528181613e680152818161459a01528181614678015261480b0152600061168101526000611651015260006107340152615e916000f3fe6080604052600436106104ba5760003560e01c80638d60cded11610279578063c63d75b61161015e578063d505accf116100d6578063ef8b30f71161008a578063f0f442601161006f578063f0f4426014610e96578063f340fa0114610eb6578063fc7b9c1814610ec957600080fd5b8063ef8b30f714610e56578063f04f270714610e7657600080fd5b8063d905777e116100bb578063d905777e14610dbb578063dd62ed3e14610dfe578063ddeadbb614610e3657600080fd5b8063d505accf14610d7b578063d547741f14610d9b57600080fd5b8063c923299a1161012d578063ce8c42e811610112578063ce8c42e814610d25578063ce96cb7714610d45578063d03153aa14610d6557600080fd5b8063c923299a14610ce5578063c9dd1f3c14610d0557600080fd5b8063c63d75b614610805578063c6e6f59214610c85578063c7e106d814610ca5578063c8b2877614610cc557600080fd5b80639ef00906116101f1578063b460af94116101c0578063b9385510116101a5578063b938551014610c1b578063ba08765214610c4f578063c245823414610c6f57600080fd5b8063b460af9414610bb5578063b5af090f14610bd557600080fd5b80639ef0090614610b40578063a217fddf14610b60578063a9059cbb14610b75578063b3d7f6b914610b9557600080fd5b806394bf804d116102485780639c82f2a41161022d5780639c82f2a414610ae05780639ceffcde14610b005780639e50767314610b2057600080fd5b806394bf804d14610aab57806395d89b4114610acb57600080fd5b80638d60cded146109f8578063902d14a014610a1857806391d1485414610a385780639356966d14610a8b57600080fd5b8063364bc15a1161039f57806360d54d411161031757806370897b23116102e65780637ecebe00116102cb5780637ecebe001461099f57806386001519146109cc57806387788782146109e257600080fd5b806370897b231461095257806370a082311461097257600080fd5b806360d54d41146108c657806361d027b3146108e65780636457f755146109185780636e553f651461093257600080fd5b80634ac8eb5f1161036e5780635216aeec116103535780635216aeec1461087a578063549dd8c3146108905780635892457d146108b057600080fd5b80634ac8eb5f146108455780634cdad5061461085a57600080fd5b8063364bc15a1461077d57806336568abe146107b157806338d52e0f146107d1578063402d267d1461080557600080fd5b806318160ddd116104325780632b3297f9116104015780632f2ff15d116103e65780632f2ff15d14610702578063313ce567146107225780633644e5151461076857600080fd5b80632b3297f9146106b55780632e2ddd45146106e257600080fd5b806318160ddd1461062f57806323b872dd14610645578063248a9ca3146106655780632a62a4901461069557600080fd5b8063095ea7b3116104895780630e567c811161046e5780630e567c81146105a0578063117da1ee146105c2578063158274a5146105e257600080fd5b8063095ea7b3146105605780630a28a4771461058057600080fd5b806301e1d114146104c657806301ffc9a7146104ee57806306fdde031461051e57806307a2d13a1461054057600080fd5b366104c157005b600080fd5b3480156104d257600080fd5b506104db610ede565b6040519081526020015b60405180910390f35b3480156104fa57600080fd5b5061050e61050936600461504c565b610fbc565b60405190151581526020016104e5565b34801561052a57600080fd5b50610533611055565b6040516104e591906150fc565b34801561054c57600080fd5b506104db61055b36600461510f565b6110e3565b34801561056c57600080fd5b5061050e61057b36600461514a565b611110565b34801561058c57600080fd5b506104db61059b36600461510f565b611189565b3480156105ac57600080fd5b506105c06105bb36600461510f565b6111a9565b005b3480156105ce57600080fd5b506105c06105dd36600461510f565b611285565b3480156105ee57600080fd5b5061060a73ba12222222228d8ba445958a75a0704d566bf2c881565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016104e5565b34801561063b57600080fd5b506104db60025481565b34801561065157600080fd5b5061050e610660366004615176565b61130d565b34801561067157600080fd5b506104db61068036600461510f565b60009081526006602052604090206001015490565b3480156106a157600080fd5b506104db6106b036600461510f565b611451565b3480156106c157600080fd5b50600b5461060a9073ffffffffffffffffffffffffffffffffffffffff1681565b3480156106ee57600080fd5b506105c06106fd3660046151b7565b611504565b34801561070e57600080fd5b506105c061071d3660046151e3565b611623565b34801561072e57600080fd5b506107567f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016104e5565b34801561077457600080fd5b506104db61164d565b34801561078957600080fd5b506104db7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b3480156107bd57600080fd5b506105c06107cc3660046151e3565b6116a3565b3480156107dd57600080fd5b5061060a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561081157600080fd5b506104db610820366004615213565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b34801561085157600080fd5b506104db611757565b34801561086657600080fd5b506104db61087536600461510f565b611833565b34801561088657600080fd5b506104db60105481565b34801561089c57600080fd5b506105c06108ab366004615272565b61183e565b3480156108bc57600080fd5b506104db60095481565b3480156108d257600080fd5b506105c06108e1366004615213565b611928565b3480156108f257600080fd5b5060075461060a90610100900473ffffffffffffffffffffffffffffffffffffffff1681565b34801561092457600080fd5b5060075461050e9060ff1681565b34801561093e57600080fd5b506104db61094d3660046151e3565b611ac1565b34801561095e57600080fd5b506105c061096d36600461510f565b611bdd565b34801561097e57600080fd5b506104db61098d366004615213565b60036020526000908152604090205481565b3480156109ab57600080fd5b506104db6109ba366004615213565b60056020526000908152604090205481565b3480156109d857600080fd5b506104db60115481565b3480156109ee57600080fd5b506104db60085481565b348015610a0457600080fd5b5061050e610a1336600461510f565b611c5e565b348015610a2457600080fd5b506105c0610a3336600461510f565b611c6b565b348015610a4457600080fd5b5061050e610a533660046151e3565b600091825260066020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b348015610a9757600080fd5b506105c0610aa63660046152be565b611caa565b348015610ab757600080fd5b506104db610ac63660046151e3565b611ec6565b348015610ad757600080fd5b50610533611f6f565b348015610aec57600080fd5b506105c0610afb366004615213565b611f7c565b348015610b0c57600080fd5b506105c0610b1b366004615356565b612046565b348015610b2c57600080fd5b506105c0610b3b366004615382565b6121d9565b348015610b4c57600080fd5b506105c0610b5b3660046153a4565b612446565b348015610b6c57600080fd5b506104db600081565b348015610b8157600080fd5b5061050e610b9036600461514a565b612564565b348015610ba157600080fd5b506104db610bb036600461510f565b6125e9565b348015610bc157600080fd5b506104db610bd036600461541f565b612608565b348015610be157600080fd5b5061050e610bf0366004615213565b73ffffffffffffffffffffffffffffffffffffffff166000908152600f602052604090205460ff1690565b348015610c2757600080fd5b5061060a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610c5b57600080fd5b506104db610c6a36600461541f565b61283c565b348015610c7b57600080fd5b506104db60125481565b348015610c9157600080fd5b506104db610ca036600461510f565b612ad2565b348015610cb157600080fd5b506105c0610cc0366004615461565b612af2565b348015610cd157600080fd5b506105c0610ce03660046151b7565b612bd5565b348015610cf157600080fd5b506105c0610d0036600461510f565b612c68565b348015610d1157600080fd5b506104db610d2036600461510f565b612ce9565b348015610d3157600080fd5b506105c0610d4036600461510f565b612d5f565b348015610d5157600080fd5b506104db610d60366004615213565b612d73565b348015610d7157600080fd5b506104db600a5481565b348015610d8757600080fd5b506105c0610d9636600461548d565b612da2565b348015610da757600080fd5b506105c0610db63660046151e3565b6130c1565b348015610dc757600080fd5b506104db610dd6366004615213565b73ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205490565b348015610e0a57600080fd5b506104db610e19366004615504565b600460209081526000928352604080842090915290825290205481565b348015610e4257600080fd5b5061060a610e5136600461510f565b6130e6565b348015610e6257600080fd5b506104db610e7136600461510f565b6130f3565b348015610e8257600080fd5b506105c0610e913660046156d6565b6130fe565b348015610ea257600080fd5b506105c0610eb1366004615213565b6131cc565b6104db610ec4366004615213565b61329d565b348015610ed557600080fd5b506104db6133fa565b6000610ee86134d0565b9050610ef26133fa565b610efc9082615810565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610f89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fad9190615823565b610fb7908261583c565b905090565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b00000000000000000000000000000000000000000000000000000000148061104f57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b600080546110629061584f565b80601f016020809104026020016040519081016040528092919081815260200182805461108e9061584f565b80156110db5780601f106110b0576101008083540402835291602001916110db565b820191906000526020600020905b8154815290600101906020018083116110be57829003601f168201915b505050505081565b6002546000908015611107576111026110fa610ede565b849083613575565b611109565b825b9392505050565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906111789086815260200190565b60405180910390a350600192915050565b600254600090801561110757611102816111a1610ede565b8591906135b1565b6111b16135f5565b60405160248101829052611281907f7ec0689f00000000000000000000000000000000000000000000000000000000906044015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152600b5473ffffffffffffffffffffffffffffffffffffffff1690613609565b5050565b61128d61362e565b670de0b6b3a76400008111156112cf576040517fc31c0b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a81905560405181815233907fb3a17cf8bbe3a4348266beede3a365af15dd59203021a6c1121d00ca2d5019f7906020015b60405180910390a250565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146113a15761136f8382615810565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff8516600090815260036020526040812080548592906113d6908490615810565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061143e9087815260200190565b60405180910390a3506001949350505050565b600061145c82611c5e565b61146857506000919050565b611473600c83613696565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c9906024015b602060405180830381865afa1580156114e0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104f9190615823565b61150c6135f5565b6000611519600c85613696565b90506115bc8163371fd8e660e01b8560405160240161153a91815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526136a2565b6115db81632e1a7d4d60e01b8460405160240161153a91815260200190565b60408051858152602081018590529081018390527fcc438ae21763799a9f64a28c62b3f15f0b5da604b7ca35e17049605a6c59599f906060015b60405180910390a150505050565b60008281526006602052604090206001015461163e816136c2565b61164883836136cc565b505050565b60007f0000000000000000000000000000000000000000000000000000000000000000461461167e57610fb76137c0565b507f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff8116331461174d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b611281828261385a565b600080611764600c613915565b90506000805b8281101561182d5761177d600c82613920565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015290935073ffffffffffffffffffffffffffffffffffffffff84169150639b56d6c990602401602060405180830381865afa1580156117eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180f9190615823565b611819908561583c565b9350806118258161589c565b91505061176a565b50505090565b600061104f826110e3565b6118466135f5565b61184f8361393e565b6118f08363e190febc60e01b848460405160240161186e92919061591d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613980565b6040518381527fed6771ea2f01816faa7ccf034b76201581d0b4374e86f83b06a8b4191c6b9f899060200160405180910390a1505050565b61193061362e565b60008173ffffffffffffffffffffffffffffffffffffffff1663af640d0f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561197d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a19190615823565b90506119ac81611c5e565b156119e6576040517f254de06900000000000000000000000000000000000000000000000000000000815260048101829052602401611744565b6119f2600c82846139ab565b506040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8757b15b00000000000000000000000000000000000000000000000000000000179052611a6b9073ffffffffffffffffffffffffffffffffffffffff841690613609565b506040805182815273ffffffffffffffffffffffffffffffffffffffff8416602082015233917f7ae8ce35a1794068c85a72f63d16209787d9e4f3e4b3095cf7f0e1de1e5e074191015b60405180910390a25050565b6000611acc836130f3565b905080600003611b38576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152606401611744565b611b7a73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330866139d6565b611b848282613a95565b604080518481526020810183905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d791015b60405180910390a361104f565b611be561362e565b670de0b6b3a7640000811115611c27576040517fc9034e1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881905560405181815233907f48d9d467a6699d3c9f09688328d42ebb56b0d1598687d06bebbe2e7f4264156290602001611302565b600061104f600c83613b0e565b611c7361362e565b601281905560405181815233907fb2bcb0a4720f48272ebaa5d990545b2ed974c8b87baec4514561aeedf05b9fc090602001611302565b611cb2613b1a565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611d3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d609190615823565b841115611d99576040517f2ca192fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360106000828254611dab919061583c565b90915550611dc4905083611dbf8385615931565b613b82565b611dcc613d49565b7f83387a3342ff1ebc5e437dc9ae0f98274afda12a11cf547eebec05a3e0b8f8a7611df5611757565b611dfd6133fa565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eab9190615823565b60408051938452602084019290925290820152606001611615565b6000611ed1836125e9565b9050611f1573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330846139d6565b611f1f8284613a95565b604080518281526020810185905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79101611bd0565b600180546110629061584f565b611f8461362e565b73ffffffffffffffffffffffffffffffffffffffff8116611fd1576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405190815233907ffb7a5f1d35a7022d9d6343bfc9a25035829d0ea72da06978793c945b1d94a17f90602001611302565b61204e61362e565b6120578261393e565b801580156120ff5750600061206d600c84613696565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c990602401602060405180830381865afa1580156120d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120fd9190615823565b115b15612139576040517f254de06900000000000000000000000000000000000000000000000000000000815260048101839052602401611744565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9265a7d50000000000000000000000000000000000000000000000000000000017905261219b908390613980565b6121a6600c83613e16565b5060405182815233907fce27e7409a7e5bc40ecb15d4e938f30ecc5e1e44b03b8f9517eb0a3bf906eccc90602001611ab5565b6121e16135f5565b670de0b6b3a7640000811115612223576040517fc31c0b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600090737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0906370a0823190602401602060405180830381865afa15801561228e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122b29190615823565b9050808311156122c0578092505b6040517fde0e9a3e00000000000000000000000000000000000000000000000000000000815260048101849052600090737f39c581f595b53c5cb19bd0b3f8da6c935e2ca09063de0e9a3e906024016020604051808303816000875af115801561232e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123529190615823565b905060006123fc847f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16631a612da6856040518263ffffffff1660e01b81526004016123b591815260200190565b602060405180830381865afa1580156123d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f69190615823565b90613e22565b604051602481018490526044810182905290915061243e907f0d3d0a6400000000000000000000000000000000000000000000000000000000906064016111e5565b505050505050565b61244e6135f5565b73ffffffffffffffffffffffffffffffffffffffff85166000908152600f602052604090205460ff166124c5576040517fcc2342f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86166004820152602401611744565b60006124ed631f2913d260e01b8888888689896040516024016111e5969594939291906159a5565b90507fd5a1cd88ddd329cc1ddf861a21708efa9ef3136b88e1420c21f91ef6f95ad0248786838060200190518101906125269190615823565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401929092529082015260600160405180910390a150505050505050565b33600090815260036020526040812080548391908390612585908490615810565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906111789086815260200190565b600254600090801561110757611102612600610ede565b8490836135b1565b600061261384611189565b90503373ffffffffffffffffffffffffffffffffffffffff8316146126c85773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146126c6576126948282615810565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6126d28482613e37565b6126dc8282613f1c565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612769573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061278d9190615823565b90508085111561279b578094505b604080518681526020810184905273ffffffffffffffffffffffffffffffffffffffff808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a461283473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168587613faa565b509392505050565b60003373ffffffffffffffffffffffffffffffffffffffff8316146128f15773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146128ef576128bd8582615810565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6128fa84611833565b905080600003612966576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f4153534554530000000000000000000000000000000000000000006044820152606401611744565b6129708185613e37565b61297a8285613f1c565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612a07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a2b9190615823565b905080821115612a39578091505b604080518381526020810187905273ffffffffffffffffffffffffffffffffffffffff808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a461283473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168584613faa565b60025460009080156111075761110281612aea610ede565b859190613575565b612afa61362e565b73ffffffffffffffffffffffffffffffffffffffff8216612b47576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152600f602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527fef81a9943b96c8df4ef243401c9bf5159146166211356898b52d382086168d92910160405180910390a15050565b612bdd6135f5565b6000612bea600c85613696565b9050612c0b81633540302360e01b8560405160240161153a91815260200190565b612c2a8163c5ebeaec60e01b8460405160240161153a91815260200190565b60408051858152602081018590529081018390527f9d9f44c86bd2cc36321fd3804a52754697914b32070b66b56b07270ef44fed1d90606001611615565b612c7061362e565b670de0b6b3a7640000811115612cb2576040517f7bfb537900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600981905560405181815233907fb414942c66de9d94d6f12b7818ecb6c326ee7f58980297cfba9a54b979e01d3390602001611302565b6000612cf482611c5e565b612d0057506000919050565b612d0b600c83613696565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639a78e72e906024016114c3565b612d67613b1a565b612d7081614063565b50565b73ffffffffffffffffffffffffffffffffffffffff811660009081526003602052604081205461104f906110e3565b42841015612e0c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401611744565b60006001612e1861164d565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612f6a573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590612fe557508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b61304b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401611744565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6000828152600660205260409020600101546130dc816136c2565b611648838361385a565b6000611109600c8361470a565b600061104f82612ad2565b613106614719565b60008180602001905181019061311c91906159f7565b905061312781614755565b6131bd73ba12222222228d8ba445958a75a0704d566bf2c88460008151811061315257613152615ae4565b60200260200101518660008151811061316d5761316d615ae4565b602002602001015161317f919061583c565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169190613faa565b6131c56147da565b5050505050565b6131d461362e565b73ffffffffffffffffffffffffffffffffffffffff8116613221576040517f91f7acdb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010073ffffffffffffffffffffffffffffffffffffffff84169081029190911790915560405190815233907f4ab5be82436d353e61ca18726e984e561f5c1cc7c6d38b29d2553c790434705a90602001611302565b6000346132a9816130f3565b915081600003613315576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152606401611744565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561337d57600080fd5b505af1158015613391573d6000803e3d6000fd5b50505050506133a08383613a95565b604080518281526020810184905273ffffffffffffffffffffffffffffffffffffffff85169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a35b50919050565b600080613407600c613915565b90506000805b8281101561182d57613420600c82613920565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015290935073ffffffffffffffffffffffffffffffffffffffff84169150639a78e72e90602401602060405180830381865afa15801561348e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134b29190615823565b6134bc908561583c565b9350806134c88161589c565b91505061340d565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16639023054a613516611757565b6040518263ffffffff1660e01b815260040161353491815260200190565b602060405180830381865afa158015613551573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb79190615823565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04841183021582026135aa57600080fd5b5091020490565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04841183021582026135e657600080fd5b50910281810615159190040190565b60075460ff1661360757613607613b1a565b565b60606111098383604051806060016040528060278152602001615e35602791396148d5565b3360009081527f54cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4f8602052604090205460ff16613607576040517f06d919f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611109838361495a565b61164873ffffffffffffffffffffffffffffffffffffffff831682613609565b612d7081336149e4565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff1661128157600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556137623390565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516137f29190615b13565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff161561128157600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b600061104f82614a9e565b600080808061392f8686614aa9565b909450925050505b9250929050565b61394781611c5e565b612d70576040517f10028b5400000000000000000000000000000000000000000000000000000000815260048101829052602401611744565b6116488161398f600c85613696565b73ffffffffffffffffffffffffffffffffffffffff1690613609565b60006139ce848473ffffffffffffffffffffffffffffffffffffffff8516614ad4565b949350505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806131c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401611744565b8060026000828254613aa7919061583c565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b60006111098383614af1565b3360009081527f102c8bff359ba7f792f3597c956360de34af36b3eee541f0c28549f821bb5369602052604090205460ff16613607576040517f04e1fa9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001808252818301909252600091602080830190803683370190505090507f000000000000000000000000000000000000000000000000000000000000000081600081518110613bd857613bd8615ae4565b73ffffffffffffffffffffffffffffffffffffffff92909216602092830291909101909101526040805160018082528183019092526000918160200160208202803683370190505090508381600081518110613c3657613c36615ae4565b602002602001018181525050613c72600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b73ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16635c38449e30848487604051602001613cb69190615be9565b6040516020818303038152906040526040518563ffffffff1660e01b8152600401613ce49493929190615c69565b600060405180830381600087803b158015613cfe57600080fd5b505af1158015613d12573d6000803e3d6000fd5b50505050613d43600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b50505050565b6010546000613d566133fa565b613d5e6134d0565b613d689190615810565b9050818111156112815760108190556000613d838383615810565b90508060116000828254613d97919061583c565b9091555050600854600090613dad908390613e22565b600754909150613de090610100900473ffffffffffffffffffffffffffffffffffffffff16613ddb83612ad2565b613a95565b60408051838152602081018390527ffa07446fad45314351eb89109a154880278451332bb87f1824d435fe58da59399101611615565b60006111098383614afd565b60006111098383670de0b6b3a7640000613575565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015613ec4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ee89190615823565b9050808311613ef657505050565b60008160125485613f07919061583c565b613f119190615810565b9050613d4381614063565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290613f51908490615810565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001613b02565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080613d43576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401611744565b600061406f600c613915565b905060008061407c6133fa565b6140846134d0565b61408e9190615810565b9050600061409d84600161583c565b67ffffffffffffffff8111156140b5576140b5615532565b6040519080825280602002602001820160405280156140e857816020015b60608152602001906001900390816140d35790505b509050818511156140f7578194505b60008060008060005b8881101561449057614113600c82613920565b6040517f9b56d6c9000000000000000000000000000000000000000000000000000000008152306004820152919650945060009073ffffffffffffffffffffffffffffffffffffffff861690639b56d6c990602401602060405180830381865afa158015614185573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141a99190615823565b9050806000036141b9575061447e565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff871690639a78e72e90602401602060405180830381865afa158015614226573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061424a9190615823565b90506000817f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16639023054a856040518263ffffffff1660e01b81526004016142aa91815260200190565b602060405180830381865afa1580156142c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142eb9190615823565b6142f59190615810565b90506143028d828c613575565b945061430f858383613575565b955061431b868c61583c565b9a507f2e2ddd4500000000000000000000000000000000000000000000000000000000888773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663934e6a436143868a8461583c565b6040518263ffffffff1660e01b81526004016143a491815260200190565b602060405180830381865afa1580156143c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143e59190615823565b604051602481019390935260448301919091526064820152608401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505089858151811061446f5761446f615ae4565b60200260200101819052505050505b806144888161589c565b915050614100565b5088601060008282546144a39190615810565b9091555050600a54604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248201526044808201939093528151808203909301835260640190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9e50767300000000000000000000000000000000000000000000000000000000179052855186908a90811061454957614549615ae4565b60209081029190910101526040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156145e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146059190615823565b90506146118887613b82565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f7e472eae96e977766c8a0c6d0157161571f966cac5533f0acc3632d2cb8bf20f90829073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156146bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146e39190615823565b6146ed9190615810565b60405190815260200160405180910390a150505050505050505050565b600080808061392f8686614b1a565b60075460ff16613607576040517fe17c49b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81518110156112815781818151811061477357614773615ae4565b602002602001015151600003156147c8576147c682828151811061479957614799615ae4565b60200260200101513073ffffffffffffffffffffffffffffffffffffffff1661360990919063ffffffff16565b505b806147d28161589c565b915050614758565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015614867573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061488b9190615823565b60125490915080821015611281576040517fcd62da430000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401611744565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516148ff9190615d1c565b600060405180830381855af49150503d806000811461493a576040519150601f19603f3d011682016040523d82523d6000602084013e61493f565b606091505b509150915061495086838387614b54565b9695505050505050565b60008181526002830160205260408120548015158061497e575061497e8484614af1565b611109576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b657900006044820152606401611744565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff1661128157614a2481614bf4565b614a2f836020614c13565b604051602001614a40929190615d38565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a0000000000000000000000000000000000000000000000000000000008252611744916004016150fc565b600061104f82614e56565b60008080614ab78585614e60565b600081815260029690960160205260409095205494959350505050565b600082815260028401602052604081208290556139ce8484614e6c565b60006111098383614e78565b600081815260028301602052604081208190556111098383614e90565b6000818152600283016020526040812054819080614b4957614b3c8585614af1565b9250600091506139379050565b600192509050613937565b60608315614bea578251600003614be35773ffffffffffffffffffffffffffffffffffffffff85163b614be3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611744565b50816139ce565b6139ce8383614e9c565b606061104f73ffffffffffffffffffffffffffffffffffffffff831660145b60606000614c22836002615db9565b614c2d90600261583c565b67ffffffffffffffff811115614c4557614c45615532565b6040519080825280601f01601f191660200182016040528015614c6f576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110614ca657614ca6615ae4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110614d0957614d09615ae4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000614d45846002615db9565b614d5090600161583c565b90505b6001811115614ded577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110614d9157614d91615ae4565b1a60f81b828281518110614da757614da7615ae4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93614de681615dd0565b9050614d53565b508315611109576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401611744565b600061104f825490565b60006111098383614ee0565b60006111098383614f0a565b60008181526001830160205260408120541515611109565b60006111098383614f59565b815115614eac5781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161174491906150fc565b6000826000018281548110614ef757614ef7615ae4565b9060005260206000200154905092915050565b6000818152600183016020526040812054614f515750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561104f565b50600061104f565b60008181526001830160205260408120548015615042576000614f7d600183615810565b8554909150600090614f9190600190615810565b9050818114614ff6576000866000018281548110614fb157614fb1615ae4565b9060005260206000200154905080876000018481548110614fd457614fd4615ae4565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061500757615007615e05565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061104f565b600091505061104f565b60006020828403121561505e57600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461110957600080fd5b60005b838110156150a9578181015183820152602001615091565b50506000910152565b600081518084526150ca81602086016020860161508e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061110960208301846150b2565b60006020828403121561512157600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114612d7057600080fd5b6000806040838503121561515d57600080fd5b823561516881615128565b946020939093013593505050565b60008060006060848603121561518b57600080fd5b833561519681615128565b925060208401356151a681615128565b929592945050506040919091013590565b6000806000606084860312156151cc57600080fd5b505081359360208301359350604090920135919050565b600080604083850312156151f657600080fd5b82359150602083013561520881615128565b809150509250929050565b60006020828403121561522557600080fd5b813561110981615128565b60008083601f84011261524257600080fd5b50813567ffffffffffffffff81111561525a57600080fd5b60208301915083602082850101111561393757600080fd5b60008060006040848603121561528757600080fd5b83359250602084013567ffffffffffffffff8111156152a557600080fd5b6152b186828701615230565b9497909650939450505050565b600080600080606085870312156152d457600080fd5b8435935060208501359250604085013567ffffffffffffffff808211156152fa57600080fd5b818701915087601f83011261530e57600080fd5b81358181111561531d57600080fd5b8860208260051b850101111561533257600080fd5b95989497505060200194505050565b8035801515811461535157600080fd5b919050565b6000806040838503121561536957600080fd5b8235915061537960208401615341565b90509250929050565b6000806040838503121561539557600080fd5b50508035926020909101359150565b60008060008060008060a087890312156153bd57600080fd5b86356153c881615128565b955060208701356153d881615128565b945060408701359350606087013567ffffffffffffffff8111156153fb57600080fd5b61540789828a01615230565b979a9699509497949695608090950135949350505050565b60008060006060848603121561543457600080fd5b83359250602084013561544681615128565b9150604084013561545681615128565b809150509250925092565b6000806040838503121561547457600080fd5b823561547f81615128565b915061537960208401615341565b600080600080600080600060e0888a0312156154a857600080fd5b87356154b381615128565b965060208801356154c381615128565b95506040880135945060608801359350608088013560ff811681146154e757600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561551757600080fd5b823561552281615128565b9150602083013561520881615128565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156155a8576155a8615532565b604052919050565b600067ffffffffffffffff8211156155ca576155ca615532565b5060051b60200190565b600082601f8301126155e557600080fd5b813560206155fa6155f5836155b0565b615561565b82815260059290921b8401810191818101908684111561561957600080fd5b8286015b84811015615634578035835291830191830161561d565b509695505050505050565b600067ffffffffffffffff82111561565957615659615532565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f83011261569657600080fd5b81356156a46155f58261563f565b8181528460208386010111156156b957600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080608085870312156156ec57600080fd5b843567ffffffffffffffff8082111561570457600080fd5b818701915087601f83011261571857600080fd5b813560206157286155f5836155b0565b82815260059290921b8401810191818101908b84111561574757600080fd5b948201945b8386101561576e57853561575f81615128565b8252948201949082019061574c565b9850508801359250508082111561578457600080fd5b615790888389016155d4565b945060408701359150808211156157a657600080fd5b6157b2888389016155d4565b935060608701359150808211156157c857600080fd5b506157d587828801615685565b91505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561104f5761104f6157e1565b60006020828403121561583557600080fd5b5051919050565b8082018082111561104f5761104f6157e1565b600181811c9082168061586357607f821691505b6020821081036133f4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036158cd576158cd6157e1565b5060010190565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6020815260006139ce6020830184866158d4565b600061593f6155f5846155b0565b80848252602080830192508560051b85013681111561595d57600080fd5b855b8181101561599957803567ffffffffffffffff81111561597f5760008081fd5b61598b36828a01615685565b86525093820193820161595f565b50919695505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a060808301526159eb60a0830184866158d4565b98975050505050505050565b60006020808385031215615a0a57600080fd5b825167ffffffffffffffff80821115615a2257600080fd5b818501915085601f830112615a3657600080fd5b8151615a446155f5826155b0565b81815260059190911b83018401908481019088831115615a6357600080fd5b8585015b83811015615ad757805185811115615a7f5760008081fd5b8601603f81018b13615a915760008081fd5b878101516040615aa36155f58361563f565b8281528d82848601011115615ab85760008081fd5b615ac7838c830184870161508e565b8652505050918601918601615a67565b5098975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080835481600182811c915080831680615b2f57607f831692505b60208084108203615b67577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b818015615b7b5760018114615bae57615bdb565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650615bdb565b60008a81526020902060005b86811015615bd35781548b820152908501908301615bba565b505084890196505b509498975050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015615c5c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452615c4a8583516150b2565b94509285019290850190600101615c10565b5092979650505050505050565b60006080820173ffffffffffffffffffffffffffffffffffffffff8088168452602060808186015282885180855260a087019150828a01945060005b81811015615cc3578551851683529483019491830191600101615ca5565b5050858103604087015287518082529082019350915080870160005b83811015615cfb57815185529382019390820190600101615cdf565b505050508281036060840152615d1181856150b2565b979650505050505050565b60008251615d2e81846020870161508e565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615d7081601785016020880161508e565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351615dad81602884016020880161508e565b01602801949350505050565b808202811582820484141761104f5761104f6157e1565b600081615ddf57615ddf6157e1565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e0d98aeb8e2ebb2f5fcca159239a137779099263e7d3cea2d855eefe25649b7a64736f6c6343000815003300000000000000000000000084f67f75daf6d57aef500e0c85c77b7b3bbc92a900000000000000000000000006444b9f0c6a966b8b9bc1e808d2b165a87e3a38000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006649f12b5ef495a3861b21e3206b1abfa33a6531000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca

Deployed Bytecode

0x6080604052600436106104ba5760003560e01c80638d60cded11610279578063c63d75b61161015e578063d505accf116100d6578063ef8b30f71161008a578063f0f442601161006f578063f0f4426014610e96578063f340fa0114610eb6578063fc7b9c1814610ec957600080fd5b8063ef8b30f714610e56578063f04f270714610e7657600080fd5b8063d905777e116100bb578063d905777e14610dbb578063dd62ed3e14610dfe578063ddeadbb614610e3657600080fd5b8063d505accf14610d7b578063d547741f14610d9b57600080fd5b8063c923299a1161012d578063ce8c42e811610112578063ce8c42e814610d25578063ce96cb7714610d45578063d03153aa14610d6557600080fd5b8063c923299a14610ce5578063c9dd1f3c14610d0557600080fd5b8063c63d75b614610805578063c6e6f59214610c85578063c7e106d814610ca5578063c8b2877614610cc557600080fd5b80639ef00906116101f1578063b460af94116101c0578063b9385510116101a5578063b938551014610c1b578063ba08765214610c4f578063c245823414610c6f57600080fd5b8063b460af9414610bb5578063b5af090f14610bd557600080fd5b80639ef0090614610b40578063a217fddf14610b60578063a9059cbb14610b75578063b3d7f6b914610b9557600080fd5b806394bf804d116102485780639c82f2a41161022d5780639c82f2a414610ae05780639ceffcde14610b005780639e50767314610b2057600080fd5b806394bf804d14610aab57806395d89b4114610acb57600080fd5b80638d60cded146109f8578063902d14a014610a1857806391d1485414610a385780639356966d14610a8b57600080fd5b8063364bc15a1161039f57806360d54d411161031757806370897b23116102e65780637ecebe00116102cb5780637ecebe001461099f57806386001519146109cc57806387788782146109e257600080fd5b806370897b231461095257806370a082311461097257600080fd5b806360d54d41146108c657806361d027b3146108e65780636457f755146109185780636e553f651461093257600080fd5b80634ac8eb5f1161036e5780635216aeec116103535780635216aeec1461087a578063549dd8c3146108905780635892457d146108b057600080fd5b80634ac8eb5f146108455780634cdad5061461085a57600080fd5b8063364bc15a1461077d57806336568abe146107b157806338d52e0f146107d1578063402d267d1461080557600080fd5b806318160ddd116104325780632b3297f9116104015780632f2ff15d116103e65780632f2ff15d14610702578063313ce567146107225780633644e5151461076857600080fd5b80632b3297f9146106b55780632e2ddd45146106e257600080fd5b806318160ddd1461062f57806323b872dd14610645578063248a9ca3146106655780632a62a4901461069557600080fd5b8063095ea7b3116104895780630e567c811161046e5780630e567c81146105a0578063117da1ee146105c2578063158274a5146105e257600080fd5b8063095ea7b3146105605780630a28a4771461058057600080fd5b806301e1d114146104c657806301ffc9a7146104ee57806306fdde031461051e57806307a2d13a1461054057600080fd5b366104c157005b600080fd5b3480156104d257600080fd5b506104db610ede565b6040519081526020015b60405180910390f35b3480156104fa57600080fd5b5061050e61050936600461504c565b610fbc565b60405190151581526020016104e5565b34801561052a57600080fd5b50610533611055565b6040516104e591906150fc565b34801561054c57600080fd5b506104db61055b36600461510f565b6110e3565b34801561056c57600080fd5b5061050e61057b36600461514a565b611110565b34801561058c57600080fd5b506104db61059b36600461510f565b611189565b3480156105ac57600080fd5b506105c06105bb36600461510f565b6111a9565b005b3480156105ce57600080fd5b506105c06105dd36600461510f565b611285565b3480156105ee57600080fd5b5061060a73ba12222222228d8ba445958a75a0704d566bf2c881565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016104e5565b34801561063b57600080fd5b506104db60025481565b34801561065157600080fd5b5061050e610660366004615176565b61130d565b34801561067157600080fd5b506104db61068036600461510f565b60009081526006602052604090206001015490565b3480156106a157600080fd5b506104db6106b036600461510f565b611451565b3480156106c157600080fd5b50600b5461060a9073ffffffffffffffffffffffffffffffffffffffff1681565b3480156106ee57600080fd5b506105c06106fd3660046151b7565b611504565b34801561070e57600080fd5b506105c061071d3660046151e3565b611623565b34801561072e57600080fd5b506107567f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff90911681526020016104e5565b34801561077457600080fd5b506104db61164d565b34801561078957600080fd5b506104db7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b3480156107bd57600080fd5b506105c06107cc3660046151e3565b6116a3565b3480156107dd57600080fd5b5061060a7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b34801561081157600080fd5b506104db610820366004615213565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b34801561085157600080fd5b506104db611757565b34801561086657600080fd5b506104db61087536600461510f565b611833565b34801561088657600080fd5b506104db60105481565b34801561089c57600080fd5b506105c06108ab366004615272565b61183e565b3480156108bc57600080fd5b506104db60095481565b3480156108d257600080fd5b506105c06108e1366004615213565b611928565b3480156108f257600080fd5b5060075461060a90610100900473ffffffffffffffffffffffffffffffffffffffff1681565b34801561092457600080fd5b5060075461050e9060ff1681565b34801561093e57600080fd5b506104db61094d3660046151e3565b611ac1565b34801561095e57600080fd5b506105c061096d36600461510f565b611bdd565b34801561097e57600080fd5b506104db61098d366004615213565b60036020526000908152604090205481565b3480156109ab57600080fd5b506104db6109ba366004615213565b60056020526000908152604090205481565b3480156109d857600080fd5b506104db60115481565b3480156109ee57600080fd5b506104db60085481565b348015610a0457600080fd5b5061050e610a1336600461510f565b611c5e565b348015610a2457600080fd5b506105c0610a3336600461510f565b611c6b565b348015610a4457600080fd5b5061050e610a533660046151e3565b600091825260066020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b348015610a9757600080fd5b506105c0610aa63660046152be565b611caa565b348015610ab757600080fd5b506104db610ac63660046151e3565b611ec6565b348015610ad757600080fd5b50610533611f6f565b348015610aec57600080fd5b506105c0610afb366004615213565b611f7c565b348015610b0c57600080fd5b506105c0610b1b366004615356565b612046565b348015610b2c57600080fd5b506105c0610b3b366004615382565b6121d9565b348015610b4c57600080fd5b506105c0610b5b3660046153a4565b612446565b348015610b6c57600080fd5b506104db600081565b348015610b8157600080fd5b5061050e610b9036600461514a565b612564565b348015610ba157600080fd5b506104db610bb036600461510f565b6125e9565b348015610bc157600080fd5b506104db610bd036600461541f565b612608565b348015610be157600080fd5b5061050e610bf0366004615213565b73ffffffffffffffffffffffffffffffffffffffff166000908152600f602052604090205460ff1690565b348015610c2757600080fd5b5061060a7f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca81565b348015610c5b57600080fd5b506104db610c6a36600461541f565b61283c565b348015610c7b57600080fd5b506104db60125481565b348015610c9157600080fd5b506104db610ca036600461510f565b612ad2565b348015610cb157600080fd5b506105c0610cc0366004615461565b612af2565b348015610cd157600080fd5b506105c0610ce03660046151b7565b612bd5565b348015610cf157600080fd5b506105c0610d0036600461510f565b612c68565b348015610d1157600080fd5b506104db610d2036600461510f565b612ce9565b348015610d3157600080fd5b506105c0610d4036600461510f565b612d5f565b348015610d5157600080fd5b506104db610d60366004615213565b612d73565b348015610d7157600080fd5b506104db600a5481565b348015610d8757600080fd5b506105c0610d9636600461548d565b612da2565b348015610da757600080fd5b506105c0610db63660046151e3565b6130c1565b348015610dc757600080fd5b506104db610dd6366004615213565b73ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205490565b348015610e0a57600080fd5b506104db610e19366004615504565b600460209081526000928352604080842090915290825290205481565b348015610e4257600080fd5b5061060a610e5136600461510f565b6130e6565b348015610e6257600080fd5b506104db610e7136600461510f565b6130f3565b348015610e8257600080fd5b506105c0610e913660046156d6565b6130fe565b348015610ea257600080fd5b506105c0610eb1366004615213565b6131cc565b6104db610ec4366004615213565b61329d565b348015610ed557600080fd5b506104db6133fa565b6000610ee86134d0565b9050610ef26133fa565b610efc9082615810565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610f89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fad9190615823565b610fb7908261583c565b905090565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b00000000000000000000000000000000000000000000000000000000148061104f57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b600080546110629061584f565b80601f016020809104026020016040519081016040528092919081815260200182805461108e9061584f565b80156110db5780601f106110b0576101008083540402835291602001916110db565b820191906000526020600020905b8154815290600101906020018083116110be57829003601f168201915b505050505081565b6002546000908015611107576111026110fa610ede565b849083613575565b611109565b825b9392505050565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906111789086815260200190565b60405180910390a350600192915050565b600254600090801561110757611102816111a1610ede565b8591906135b1565b6111b16135f5565b60405160248101829052611281907f7ec0689f00000000000000000000000000000000000000000000000000000000906044015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152600b5473ffffffffffffffffffffffffffffffffffffffff1690613609565b5050565b61128d61362e565b670de0b6b3a76400008111156112cf576040517fc31c0b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a81905560405181815233907fb3a17cf8bbe3a4348266beede3a365af15dd59203021a6c1121d00ca2d5019f7906020015b60405180910390a250565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146113a15761136f8382615810565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff8516600090815260036020526040812080548592906113d6908490615810565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061143e9087815260200190565b60405180910390a3506001949350505050565b600061145c82611c5e565b61146857506000919050565b611473600c83613696565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c9906024015b602060405180830381865afa1580156114e0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104f9190615823565b61150c6135f5565b6000611519600c85613696565b90506115bc8163371fd8e660e01b8560405160240161153a91815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526136a2565b6115db81632e1a7d4d60e01b8460405160240161153a91815260200190565b60408051858152602081018590529081018390527fcc438ae21763799a9f64a28c62b3f15f0b5da604b7ca35e17049605a6c59599f906060015b60405180910390a150505050565b60008281526006602052604090206001015461163e816136c2565b61164883836136cc565b505050565b60007f0000000000000000000000000000000000000000000000000000000000000001461461167e57610fb76137c0565b507fd8923989cc8cdb0547291784e429c6f0e48e6d2389592f7d663ebe52ea9ffcd390565b73ffffffffffffffffffffffffffffffffffffffff8116331461174d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b611281828261385a565b600080611764600c613915565b90506000805b8281101561182d5761177d600c82613920565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015290935073ffffffffffffffffffffffffffffffffffffffff84169150639b56d6c990602401602060405180830381865afa1580156117eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180f9190615823565b611819908561583c565b9350806118258161589c565b91505061176a565b50505090565b600061104f826110e3565b6118466135f5565b61184f8361393e565b6118f08363e190febc60e01b848460405160240161186e92919061591d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613980565b6040518381527fed6771ea2f01816faa7ccf034b76201581d0b4374e86f83b06a8b4191c6b9f899060200160405180910390a1505050565b61193061362e565b60008173ffffffffffffffffffffffffffffffffffffffff1663af640d0f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561197d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a19190615823565b90506119ac81611c5e565b156119e6576040517f254de06900000000000000000000000000000000000000000000000000000000815260048101829052602401611744565b6119f2600c82846139ab565b506040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8757b15b00000000000000000000000000000000000000000000000000000000179052611a6b9073ffffffffffffffffffffffffffffffffffffffff841690613609565b506040805182815273ffffffffffffffffffffffffffffffffffffffff8416602082015233917f7ae8ce35a1794068c85a72f63d16209787d9e4f3e4b3095cf7f0e1de1e5e074191015b60405180910390a25050565b6000611acc836130f3565b905080600003611b38576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152606401611744565b611b7a73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2163330866139d6565b611b848282613a95565b604080518481526020810183905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d791015b60405180910390a361104f565b611be561362e565b670de0b6b3a7640000811115611c27576040517fc9034e1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881905560405181815233907f48d9d467a6699d3c9f09688328d42ebb56b0d1598687d06bebbe2e7f4264156290602001611302565b600061104f600c83613b0e565b611c7361362e565b601281905560405181815233907fb2bcb0a4720f48272ebaa5d990545b2ed974c8b87baec4514561aeedf05b9fc090602001611302565b611cb2613b1a565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611d3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d609190615823565b841115611d99576040517f2ca192fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360106000828254611dab919061583c565b90915550611dc4905083611dbf8385615931565b613b82565b611dcc613d49565b7f83387a3342ff1ebc5e437dc9ae0f98274afda12a11cf547eebec05a3e0b8f8a7611df5611757565b611dfd6133fa565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eab9190615823565b60408051938452602084019290925290820152606001611615565b6000611ed1836125e9565b9050611f1573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2163330846139d6565b611f1f8284613a95565b604080518281526020810185905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79101611bd0565b600180546110629061584f565b611f8461362e565b73ffffffffffffffffffffffffffffffffffffffff8116611fd1576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405190815233907ffb7a5f1d35a7022d9d6343bfc9a25035829d0ea72da06978793c945b1d94a17f90602001611302565b61204e61362e565b6120578261393e565b801580156120ff5750600061206d600c84613696565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c990602401602060405180830381865afa1580156120d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120fd9190615823565b115b15612139576040517f254de06900000000000000000000000000000000000000000000000000000000815260048101839052602401611744565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9265a7d50000000000000000000000000000000000000000000000000000000017905261219b908390613980565b6121a6600c83613e16565b5060405182815233907fce27e7409a7e5bc40ecb15d4e938f30ecc5e1e44b03b8f9517eb0a3bf906eccc90602001611ab5565b6121e16135f5565b670de0b6b3a7640000811115612223576040517fc31c0b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600090737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0906370a0823190602401602060405180830381865afa15801561228e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122b29190615823565b9050808311156122c0578092505b6040517fde0e9a3e00000000000000000000000000000000000000000000000000000000815260048101849052600090737f39c581f595b53c5cb19bd0b3f8da6c935e2ca09063de0e9a3e906024016020604051808303816000875af115801561232e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123529190615823565b905060006123fc847f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca73ffffffffffffffffffffffffffffffffffffffff16631a612da6856040518263ffffffff1660e01b81526004016123b591815260200190565b602060405180830381865afa1580156123d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f69190615823565b90613e22565b604051602481018490526044810182905290915061243e907f0d3d0a6400000000000000000000000000000000000000000000000000000000906064016111e5565b505050505050565b61244e6135f5565b73ffffffffffffffffffffffffffffffffffffffff85166000908152600f602052604090205460ff166124c5576040517fcc2342f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86166004820152602401611744565b60006124ed631f2913d260e01b8888888689896040516024016111e5969594939291906159a5565b90507fd5a1cd88ddd329cc1ddf861a21708efa9ef3136b88e1420c21f91ef6f95ad0248786838060200190518101906125269190615823565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401929092529082015260600160405180910390a150505050505050565b33600090815260036020526040812080548391908390612585908490615810565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906111789086815260200190565b600254600090801561110757611102612600610ede565b8490836135b1565b600061261384611189565b90503373ffffffffffffffffffffffffffffffffffffffff8316146126c85773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146126c6576126948282615810565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6126d28482613e37565b6126dc8282613f1c565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612769573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061278d9190615823565b90508085111561279b578094505b604080518681526020810184905273ffffffffffffffffffffffffffffffffffffffff808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a461283473ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168587613faa565b509392505050565b60003373ffffffffffffffffffffffffffffffffffffffff8316146128f15773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146128ef576128bd8582615810565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6128fa84611833565b905080600003612966576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f4153534554530000000000000000000000000000000000000000006044820152606401611744565b6129708185613e37565b61297a8285613f1c565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612a07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a2b9190615823565b905080821115612a39578091505b604080518381526020810187905273ffffffffffffffffffffffffffffffffffffffff808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a461283473ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168584613faa565b60025460009080156111075761110281612aea610ede565b859190613575565b612afa61362e565b73ffffffffffffffffffffffffffffffffffffffff8216612b47576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152600f602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527fef81a9943b96c8df4ef243401c9bf5159146166211356898b52d382086168d92910160405180910390a15050565b612bdd6135f5565b6000612bea600c85613696565b9050612c0b81633540302360e01b8560405160240161153a91815260200190565b612c2a8163c5ebeaec60e01b8460405160240161153a91815260200190565b60408051858152602081018590529081018390527f9d9f44c86bd2cc36321fd3804a52754697914b32070b66b56b07270ef44fed1d90606001611615565b612c7061362e565b670de0b6b3a7640000811115612cb2576040517f7bfb537900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600981905560405181815233907fb414942c66de9d94d6f12b7818ecb6c326ee7f58980297cfba9a54b979e01d3390602001611302565b6000612cf482611c5e565b612d0057506000919050565b612d0b600c83613696565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639a78e72e906024016114c3565b612d67613b1a565b612d7081614063565b50565b73ffffffffffffffffffffffffffffffffffffffff811660009081526003602052604081205461104f906110e3565b42841015612e0c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401611744565b60006001612e1861164d565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612f6a573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590612fe557508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b61304b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401611744565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6000828152600660205260409020600101546130dc816136c2565b611648838361385a565b6000611109600c8361470a565b600061104f82612ad2565b613106614719565b60008180602001905181019061311c91906159f7565b905061312781614755565b6131bd73ba12222222228d8ba445958a75a0704d566bf2c88460008151811061315257613152615ae4565b60200260200101518660008151811061316d5761316d615ae4565b602002602001015161317f919061583c565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169190613faa565b6131c56147da565b5050505050565b6131d461362e565b73ffffffffffffffffffffffffffffffffffffffff8116613221576040517f91f7acdb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010073ffffffffffffffffffffffffffffffffffffffff84169081029190911790915560405190815233907f4ab5be82436d353e61ca18726e984e561f5c1cc7c6d38b29d2553c790434705a90602001611302565b6000346132a9816130f3565b915081600003613315576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152606401611744565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561337d57600080fd5b505af1158015613391573d6000803e3d6000fd5b50505050506133a08383613a95565b604080518281526020810184905273ffffffffffffffffffffffffffffffffffffffff85169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a35b50919050565b600080613407600c613915565b90506000805b8281101561182d57613420600c82613920565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015290935073ffffffffffffffffffffffffffffffffffffffff84169150639a78e72e90602401602060405180830381865afa15801561348e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134b29190615823565b6134bc908561583c565b9350806134c88161589c565b91505061340d565b60007f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca73ffffffffffffffffffffffffffffffffffffffff16639023054a613516611757565b6040518263ffffffff1660e01b815260040161353491815260200190565b602060405180830381865afa158015613551573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb79190615823565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04841183021582026135aa57600080fd5b5091020490565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04841183021582026135e657600080fd5b50910281810615159190040190565b60075460ff1661360757613607613b1a565b565b60606111098383604051806060016040528060278152602001615e35602791396148d5565b3360009081527f54cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4f8602052604090205460ff16613607576040517f06d919f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611109838361495a565b61164873ffffffffffffffffffffffffffffffffffffffff831682613609565b612d7081336149e4565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff1661128157600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556137623390565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516137f29190615b13565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff161561128157600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b600061104f82614a9e565b600080808061392f8686614aa9565b909450925050505b9250929050565b61394781611c5e565b612d70576040517f10028b5400000000000000000000000000000000000000000000000000000000815260048101829052602401611744565b6116488161398f600c85613696565b73ffffffffffffffffffffffffffffffffffffffff1690613609565b60006139ce848473ffffffffffffffffffffffffffffffffffffffff8516614ad4565b949350505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806131c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401611744565b8060026000828254613aa7919061583c565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b60006111098383614af1565b3360009081527f102c8bff359ba7f792f3597c956360de34af36b3eee541f0c28549f821bb5369602052604090205460ff16613607576040517f04e1fa9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001808252818301909252600091602080830190803683370190505090507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281600081518110613bd857613bd8615ae4565b73ffffffffffffffffffffffffffffffffffffffff92909216602092830291909101909101526040805160018082528183019092526000918160200160208202803683370190505090508381600081518110613c3657613c36615ae4565b602002602001018181525050613c72600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b73ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16635c38449e30848487604051602001613cb69190615be9565b6040516020818303038152906040526040518563ffffffff1660e01b8152600401613ce49493929190615c69565b600060405180830381600087803b158015613cfe57600080fd5b505af1158015613d12573d6000803e3d6000fd5b50505050613d43600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b50505050565b6010546000613d566133fa565b613d5e6134d0565b613d689190615810565b9050818111156112815760108190556000613d838383615810565b90508060116000828254613d97919061583c565b9091555050600854600090613dad908390613e22565b600754909150613de090610100900473ffffffffffffffffffffffffffffffffffffffff16613ddb83612ad2565b613a95565b60408051838152602081018390527ffa07446fad45314351eb89109a154880278451332bb87f1824d435fe58da59399101611615565b60006111098383614afd565b60006111098383670de0b6b3a7640000613575565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015613ec4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ee89190615823565b9050808311613ef657505050565b60008160125485613f07919061583c565b613f119190615810565b9050613d4381614063565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290613f51908490615810565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001613b02565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080613d43576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401611744565b600061406f600c613915565b905060008061407c6133fa565b6140846134d0565b61408e9190615810565b9050600061409d84600161583c565b67ffffffffffffffff8111156140b5576140b5615532565b6040519080825280602002602001820160405280156140e857816020015b60608152602001906001900390816140d35790505b509050818511156140f7578194505b60008060008060005b8881101561449057614113600c82613920565b6040517f9b56d6c9000000000000000000000000000000000000000000000000000000008152306004820152919650945060009073ffffffffffffffffffffffffffffffffffffffff861690639b56d6c990602401602060405180830381865afa158015614185573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141a99190615823565b9050806000036141b9575061447e565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff871690639a78e72e90602401602060405180830381865afa158015614226573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061424a9190615823565b90506000817f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca73ffffffffffffffffffffffffffffffffffffffff16639023054a856040518263ffffffff1660e01b81526004016142aa91815260200190565b602060405180830381865afa1580156142c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142eb9190615823565b6142f59190615810565b90506143028d828c613575565b945061430f858383613575565b955061431b868c61583c565b9a507f2e2ddd4500000000000000000000000000000000000000000000000000000000888773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca1663934e6a436143868a8461583c565b6040518263ffffffff1660e01b81526004016143a491815260200190565b602060405180830381865afa1580156143c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143e59190615823565b604051602481019390935260448301919091526064820152608401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505089858151811061446f5761446f615ae4565b60200260200101819052505050505b806144888161589c565b915050614100565b5088601060008282546144a39190615810565b9091555050600a54604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248201526044808201939093528151808203909301835260640190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9e50767300000000000000000000000000000000000000000000000000000000179052855186908a90811061454957614549615ae4565b60209081029190910101526040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa1580156145e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146059190615823565b90506146118887613b82565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f7e472eae96e977766c8a0c6d0157161571f966cac5533f0acc3632d2cb8bf20f90829073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa1580156146bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146e39190615823565b6146ed9190615810565b60405190815260200160405180910390a150505050505050505050565b600080808061392f8686614b1a565b60075460ff16613607576040517fe17c49b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81518110156112815781818151811061477357614773615ae4565b602002602001015151600003156147c8576147c682828151811061479957614799615ae4565b60200260200101513073ffffffffffffffffffffffffffffffffffffffff1661360990919063ffffffff16565b505b806147d28161589c565b915050614758565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015614867573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061488b9190615823565b60125490915080821015611281576040517fcd62da430000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401611744565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516148ff9190615d1c565b600060405180830381855af49150503d806000811461493a576040519150601f19603f3d011682016040523d82523d6000602084013e61493f565b606091505b509150915061495086838387614b54565b9695505050505050565b60008181526002830160205260408120548015158061497e575061497e8484614af1565b611109576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b657900006044820152606401611744565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff1661128157614a2481614bf4565b614a2f836020614c13565b604051602001614a40929190615d38565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a0000000000000000000000000000000000000000000000000000000008252611744916004016150fc565b600061104f82614e56565b60008080614ab78585614e60565b600081815260029690960160205260409095205494959350505050565b600082815260028401602052604081208290556139ce8484614e6c565b60006111098383614e78565b600081815260028301602052604081208190556111098383614e90565b6000818152600283016020526040812054819080614b4957614b3c8585614af1565b9250600091506139379050565b600192509050613937565b60608315614bea578251600003614be35773ffffffffffffffffffffffffffffffffffffffff85163b614be3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611744565b50816139ce565b6139ce8383614e9c565b606061104f73ffffffffffffffffffffffffffffffffffffffff831660145b60606000614c22836002615db9565b614c2d90600261583c565b67ffffffffffffffff811115614c4557614c45615532565b6040519080825280601f01601f191660200182016040528015614c6f576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110614ca657614ca6615ae4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110614d0957614d09615ae4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000614d45846002615db9565b614d5090600161583c565b90505b6001811115614ded577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110614d9157614d91615ae4565b1a60f81b828281518110614da757614da7615ae4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93614de681615dd0565b9050614d53565b508315611109576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401611744565b600061104f825490565b60006111098383614ee0565b60006111098383614f0a565b60008181526001830160205260408120541515611109565b60006111098383614f59565b815115614eac5781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161174491906150fc565b6000826000018281548110614ef757614ef7615ae4565b9060005260206000200154905092915050565b6000818152600183016020526040812054614f515750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561104f565b50600061104f565b60008181526001830160205260408120548015615042576000614f7d600183615810565b8554909150600090614f9190600190615810565b9050818114614ff6576000866000018281548110614fb157614fb1615ae4565b9060005260206000200154905080876000018481548110614fd457614fd4615ae4565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061500757615007615e05565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061104f565b600091505061104f565b60006020828403121561505e57600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461110957600080fd5b60005b838110156150a9578181015183820152602001615091565b50506000910152565b600081518084526150ca81602086016020860161508e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061110960208301846150b2565b60006020828403121561512157600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114612d7057600080fd5b6000806040838503121561515d57600080fd5b823561516881615128565b946020939093013593505050565b60008060006060848603121561518b57600080fd5b833561519681615128565b925060208401356151a681615128565b929592945050506040919091013590565b6000806000606084860312156151cc57600080fd5b505081359360208301359350604090920135919050565b600080604083850312156151f657600080fd5b82359150602083013561520881615128565b809150509250929050565b60006020828403121561522557600080fd5b813561110981615128565b60008083601f84011261524257600080fd5b50813567ffffffffffffffff81111561525a57600080fd5b60208301915083602082850101111561393757600080fd5b60008060006040848603121561528757600080fd5b83359250602084013567ffffffffffffffff8111156152a557600080fd5b6152b186828701615230565b9497909650939450505050565b600080600080606085870312156152d457600080fd5b8435935060208501359250604085013567ffffffffffffffff808211156152fa57600080fd5b818701915087601f83011261530e57600080fd5b81358181111561531d57600080fd5b8860208260051b850101111561533257600080fd5b95989497505060200194505050565b8035801515811461535157600080fd5b919050565b6000806040838503121561536957600080fd5b8235915061537960208401615341565b90509250929050565b6000806040838503121561539557600080fd5b50508035926020909101359150565b60008060008060008060a087890312156153bd57600080fd5b86356153c881615128565b955060208701356153d881615128565b945060408701359350606087013567ffffffffffffffff8111156153fb57600080fd5b61540789828a01615230565b979a9699509497949695608090950135949350505050565b60008060006060848603121561543457600080fd5b83359250602084013561544681615128565b9150604084013561545681615128565b809150509250925092565b6000806040838503121561547457600080fd5b823561547f81615128565b915061537960208401615341565b600080600080600080600060e0888a0312156154a857600080fd5b87356154b381615128565b965060208801356154c381615128565b95506040880135945060608801359350608088013560ff811681146154e757600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561551757600080fd5b823561552281615128565b9150602083013561520881615128565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156155a8576155a8615532565b604052919050565b600067ffffffffffffffff8211156155ca576155ca615532565b5060051b60200190565b600082601f8301126155e557600080fd5b813560206155fa6155f5836155b0565b615561565b82815260059290921b8401810191818101908684111561561957600080fd5b8286015b84811015615634578035835291830191830161561d565b509695505050505050565b600067ffffffffffffffff82111561565957615659615532565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f83011261569657600080fd5b81356156a46155f58261563f565b8181528460208386010111156156b957600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080608085870312156156ec57600080fd5b843567ffffffffffffffff8082111561570457600080fd5b818701915087601f83011261571857600080fd5b813560206157286155f5836155b0565b82815260059290921b8401810191818101908b84111561574757600080fd5b948201945b8386101561576e57853561575f81615128565b8252948201949082019061574c565b9850508801359250508082111561578457600080fd5b615790888389016155d4565b945060408701359150808211156157a657600080fd5b6157b2888389016155d4565b935060608701359150808211156157c857600080fd5b506157d587828801615685565b91505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561104f5761104f6157e1565b60006020828403121561583557600080fd5b5051919050565b8082018082111561104f5761104f6157e1565b600181811c9082168061586357607f821691505b6020821081036133f4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036158cd576158cd6157e1565b5060010190565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6020815260006139ce6020830184866158d4565b600061593f6155f5846155b0565b80848252602080830192508560051b85013681111561595d57600080fd5b855b8181101561599957803567ffffffffffffffff81111561597f5760008081fd5b61598b36828a01615685565b86525093820193820161595f565b50919695505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a060808301526159eb60a0830184866158d4565b98975050505050505050565b60006020808385031215615a0a57600080fd5b825167ffffffffffffffff80821115615a2257600080fd5b818501915085601f830112615a3657600080fd5b8151615a446155f5826155b0565b81815260059190911b83018401908481019088831115615a6357600080fd5b8585015b83811015615ad757805185811115615a7f5760008081fd5b8601603f81018b13615a915760008081fd5b878101516040615aa36155f58361563f565b8281528d82848601011115615ab85760008081fd5b615ac7838c830184870161508e565b8652505050918601918601615a67565b5098975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080835481600182811c915080831680615b2f57607f831692505b60208084108203615b67577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b818015615b7b5760018114615bae57615bdb565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650615bdb565b60008a81526020902060005b86811015615bd35781548b820152908501908301615bba565b505084890196505b509498975050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015615c5c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452615c4a8583516150b2565b94509285019290850190600101615c10565b5092979650505050505050565b60006080820173ffffffffffffffffffffffffffffffffffffffff8088168452602060808186015282885180855260a087019150828a01945060005b81811015615cc3578551851683529483019491830191600101615ca5565b5050858103604087015287518082529082019350915080870160005b83811015615cfb57815185529382019390820190600101615cdf565b505050508281036060840152615d1181856150b2565b979650505050505050565b60008251615d2e81846020870161508e565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615d7081601785016020880161508e565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351615dad81602884016020880161508e565b01602801949350505050565b808202811582820484141761104f5761104f6157e1565b600081615ddf57615ddf6157e1565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e0d98aeb8e2ebb2f5fcca159239a137779099263e7d3cea2d855eefe25649b7a64736f6c63430008150033

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

00000000000000000000000084f67f75daf6d57aef500e0c85c77b7b3bbc92a900000000000000000000000006444b9f0c6a966b8b9bc1e808d2b165a87e3a38000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006649f12b5ef495a3861b21e3206b1abfa33a6531000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca

-----Decoded View---------------
Arg [0] : _admin (address): 0x84f67f75DAf6D57Aef500E0c85C77B7b3bBc92A9
Arg [1] : _keeper (address): 0x06444B9F0c6a966b8B9Bc1e808d2B165a87e3a38
Arg [2] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [3] : _swapper (address): 0x6649f12b5ef495a3861b21E3206B1AbfA33A6531
Arg [4] : _priceConverter (address): 0xD76B0Ff4A487CaFE4E19ed15B73f12f6A92095Ca

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000084f67f75daf6d57aef500e0c85c77b7b3bbc92a9
Arg [1] : 00000000000000000000000006444b9f0c6a966b8b9bc1e808d2b165a87e3a38
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 0000000000000000000000006649f12b5ef495a3861b21e3206b1abfa33a6531
Arg [4] : 000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

OVERVIEW

Sandclock ETH (scWETHv2) is a tokenized mainnet yield strategy which enters leveraged staked ETH positions on multiple lending markets using adapters. It regularly rebalances the positions against target loan to value ratios and a allocation configuration in order to turn a profit over ETH.

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.