ETH Price: $1,897.74 (-11.49%)
 

Overview

Max Total Supply

0 ERC20 ***

Holders

0

Transfers

-
0

Market

Onchain Market Cap

-

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Loading...
Loading
Loading...
Loading
Loading...
Loading

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

Contract Source Code Verified (Exact Match)

Contract Name:
scWETHv2

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
paris EvmVersion
// 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")
    {}

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

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

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

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

// 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))
        }
    }
}

// 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;
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.19;

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);

    // 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;

    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;
    }

    /**
     * @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 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 _token The token to sell.
     * @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 _token, uint256 _amount, bytes calldata _swapData, uint256 _assetAmountOutMin) external {
        _onlyKeeperOrFlashLoan();

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

        emit TokenSwapped(address(_token), _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();
    }
}

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

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

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

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

// 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;
    }
}

// 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;
}

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

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

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

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

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

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

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

// 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 {}
}

// 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;
}

// 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;
    }
}

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

// 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;
    }
}

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

// 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

API
[{"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":[],"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":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":"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":"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":"_token","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"}]

61012060405267016345785d8a0000600855662386f26fc10000600955670dbd2fc137a30000600a55670de0b6b3a76400006011553480156200004157600080fd5b50604051620063a4380380620063a48339810160408190526200006491620003d1565b84848483856040518060400160405280601781526020017f53616e64636c6f636b2057455448205661756c742076320000000000000000008152506040518060400160405280600881526020016739b1aba2aa243b1960c11b81525086868684848282828181846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000109573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200012f919062000451565b60006200013d848262000522565b5060016200014c838262000522565b5060ff81166080524660a052620001626200024f565b60c0525050506001600160a01b0392831660e05250508516620001985760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038416620001c05760405163d92e233d60e01b815260040160405180910390fd5b620001cd600086620002eb565b620001f97ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab85620002eb565b50505050506200020f846200039060201b60201c565b6200021a8362000390565b50506001600160a01b0391821661010052600b80546001600160a01b03191691909216179055506200066c9650505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620002839190620005ee565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60008281526006602090815260408083206001600160a01b038516845290915290205460ff166200038c5760008281526006602090815260408083206001600160a01b03851684529091529020805460ff191660011790556200034b3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6001600160a01b038116620003b85760405163d92e233d60e01b815260040160405180910390fd5b50565b6001600160a01b0381168114620003b857600080fd5b600080600080600060a08688031215620003ea57600080fd5b8551620003f781620003bb565b60208701519095506200040a81620003bb565b60408701519094506200041d81620003bb565b60608701519093506200043081620003bb565b60808701519092506200044381620003bb565b809150509295509295909350565b6000602082840312156200046457600080fd5b815160ff811681146200047657600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620004a857607f821691505b602082108103620004c957634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200051d57600081815260208120601f850160051c81016020861015620004f85750805b601f850160051c820191505b81811015620005195782815560010162000504565b5050505b505050565b81516001600160401b038111156200053e576200053e6200047d565b62000556816200054f845462000493565b84620004cf565b602080601f8311600181146200058e5760008415620005755750858301515b600019600386901b1c1916600185901b17855562000519565b600085815260208120601f198616915b82811015620005bf578886015182559484019460019091019084016200059e565b5085821015620005de5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000808354620005fe8162000493565b600182811680156200061957600181146200062f5762000660565b60ff198416875282151583028701945062000660565b8760005260208060002060005b85811015620006575781548a8201529084019082016200063c565b50505082870194505b50929695505050505050565b60805160a05160c05160e05161010051615c606200074460003960008181610bb1015281816122c0015281816132fd0152818161407a01526141800152600081816107ad01528181610e9101528181611ab601528181611c4401528181611d8f01528181611e51015281816123c20152818161261901528181612719015281816128b7015281816129b701528181612fbf01528181613140015281816139cf01528181613c91015281816143c3015281816144a10152614634015260006115e5015260006115b5015260006106fe0152615c606000f3fe6080604052600436106104845760003560e01c8063877887821161025e578063c63d75b611610143578063d547741f116100bb578063ef8b30f71161008a578063f0f442601161006f578063f0f4426014610dfa578063f340fa0114610e1a578063fc7b9c1814610e2d57600080fd5b8063ef8b30f714610dba578063f04f270714610dda57600080fd5b8063d547741f14610cff578063d905777e14610d1f578063dd62ed3e14610d62578063ddeadbb614610d9a57600080fd5b8063c9dd1f3c11610112578063ce96cb77116100f7578063ce96cb7714610ca9578063d03153aa14610cc9578063d505accf14610cdf57600080fd5b8063c9dd1f3c14610c69578063ce8c42e814610c8957600080fd5b8063c63d75b6146107cf578063c6e6f59214610c09578063c8b2877614610c29578063c923299a14610c4957600080fd5b80639e507673116101d6578063b3d7f6b9116101a5578063b93855101161018a578063b938551014610b9f578063ba08765214610bd3578063c245823414610bf357600080fd5b8063b3d7f6b914610b5f578063b460af9414610b7f57600080fd5b80639e50767314610aea578063a217fddf14610b0a578063a59dffd514610b1f578063a9059cbb14610b3f57600080fd5b80639356966d1161022d57806395d89b411161021257806395d89b4114610a955780639c82f2a414610aaa5780639ceffcde14610aca57600080fd5b80639356966d14610a5557806394bf804d14610a7557600080fd5b806387788782146109ac5780638d60cded146109c2578063902d14a0146109e257806391d1485414610a0257600080fd5b80633644e515116103845780635892457d116102fc5780636e553f65116102cb57806370a08231116102b057806370a082311461093c5780637ecebe0014610969578063860015191461099657600080fd5b80636e553f65146108fc57806370897b231461091c57600080fd5b80635892457d1461087a57806360d54d411461089057806361d027b3146108b05780636457f755146108e257600080fd5b8063402d267d116103535780634cdad506116103385780634cdad506146108245780635216aeec14610844578063549dd8c31461085a57600080fd5b8063402d267d146107cf5780634ac8eb5f1461080f57600080fd5b80633644e51514610732578063364bc15a1461074757806336568abe1461077b57806338d52e0f1461079b57600080fd5b8063158274a5116104175780632a62a490116103e65780632e2ddd45116103cb5780632e2ddd45146106ac5780632f2ff15d146106cc578063313ce567146106ec57600080fd5b80632a62a4901461065f5780632b3297f91461067f57600080fd5b8063158274a5146105ac57806318160ddd146105f957806323b872dd1461060f578063248a9ca31461062f57600080fd5b8063095ea7b311610453578063095ea7b31461052a5780630a28a4771461054a5780630e567c811461056a578063117da1ee1461058c57600080fd5b806301e1d1141461049057806301ffc9a7146104b857806306fdde03146104e857806307a2d13a1461050a57600080fd5b3661048b57005b600080fd5b34801561049c57600080fd5b506104a5610e42565b6040519081526020015b60405180910390f35b3480156104c457600080fd5b506104d86104d3366004614e75565b610f20565b60405190151581526020016104af565b3480156104f457600080fd5b506104fd610fb9565b6040516104af9190614f25565b34801561051657600080fd5b506104a5610525366004614f38565b611047565b34801561053657600080fd5b506104d8610545366004614f73565b611074565b34801561055657600080fd5b506104a5610565366004614f38565b6110ed565b34801561057657600080fd5b5061058a610585366004614f38565b61110d565b005b34801561059857600080fd5b5061058a6105a7366004614f38565b6111e9565b3480156105b857600080fd5b506105d473ba12222222228d8ba445958a75a0704d566bf2c881565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016104af565b34801561060557600080fd5b506104a560025481565b34801561061b57600080fd5b506104d861062a366004614f9f565b611271565b34801561063b57600080fd5b506104a561064a366004614f38565b60009081526006602052604090206001015490565b34801561066b57600080fd5b506104a561067a366004614f38565b6113b5565b34801561068b57600080fd5b50600b546105d49073ffffffffffffffffffffffffffffffffffffffff1681565b3480156106b857600080fd5b5061058a6106c7366004614fe0565b611468565b3480156106d857600080fd5b5061058a6106e736600461500c565b611587565b3480156106f857600080fd5b506107207f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016104af565b34801561073e57600080fd5b506104a56115b1565b34801561075357600080fd5b506104a57ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b34801561078757600080fd5b5061058a61079636600461500c565b611607565b3480156107a757600080fd5b506105d47f000000000000000000000000000000000000000000000000000000000000000081565b3480156107db57600080fd5b506104a56107ea36600461503c565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b34801561081b57600080fd5b506104a56116bb565b34801561083057600080fd5b506104a561083f366004614f38565b611797565b34801561085057600080fd5b506104a5600f5481565b34801561086657600080fd5b5061058a61087536600461509b565b6117a2565b34801561088657600080fd5b506104a560095481565b34801561089c57600080fd5b5061058a6108ab36600461503c565b61188c565b3480156108bc57600080fd5b506007546105d490610100900473ffffffffffffffffffffffffffffffffffffffff1681565b3480156108ee57600080fd5b506007546104d89060ff1681565b34801561090857600080fd5b506104a561091736600461500c565b611a25565b34801561092857600080fd5b5061058a610937366004614f38565b611b41565b34801561094857600080fd5b506104a561095736600461503c565b60036020526000908152604090205481565b34801561097557600080fd5b506104a561098436600461503c565b60056020526000908152604090205481565b3480156109a257600080fd5b506104a560105481565b3480156109b857600080fd5b506104a560085481565b3480156109ce57600080fd5b506104d86109dd366004614f38565b611bc2565b3480156109ee57600080fd5b5061058a6109fd366004614f38565b611bcf565b348015610a0e57600080fd5b506104d8610a1d36600461500c565b600091825260066020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b348015610a6157600080fd5b5061058a610a703660046150e7565b611c0e565b348015610a8157600080fd5b506104a5610a9036600461500c565b611e2a565b348015610aa157600080fd5b506104fd611ed3565b348015610ab657600080fd5b5061058a610ac536600461503c565b611ee0565b348015610ad657600080fd5b5061058a610ae536600461516a565b611faa565b348015610af657600080fd5b5061058a610b05366004615194565b61213d565b348015610b1657600080fd5b506104a5600081565b348015610b2b57600080fd5b5061058a610b3a3660046151b6565b6123aa565b348015610b4b57600080fd5b506104d8610b5a366004614f73565b612470565b348015610b6b57600080fd5b506104a5610b7a366004614f38565b6124f5565b348015610b8b57600080fd5b506104a5610b9a36600461521a565b612514565b348015610bab57600080fd5b506105d47f000000000000000000000000000000000000000000000000000000000000000081565b348015610bdf57600080fd5b506104a5610bee36600461521a565b612748565b348015610bff57600080fd5b506104a560115481565b348015610c1557600080fd5b506104a5610c24366004614f38565b6129de565b348015610c3557600080fd5b5061058a610c44366004614fe0565b6129fe565b348015610c5557600080fd5b5061058a610c64366004614f38565b612a91565b348015610c7557600080fd5b506104a5610c84366004614f38565b612b12565b348015610c9557600080fd5b5061058a610ca4366004614f38565b612b88565b348015610cb557600080fd5b506104a5610cc436600461503c565b612b9c565b348015610cd557600080fd5b506104a5600a5481565b348015610ceb57600080fd5b5061058a610cfa36600461525c565b612bcb565b348015610d0b57600080fd5b5061058a610d1a36600461500c565b612eea565b348015610d2b57600080fd5b506104a5610d3a36600461503c565b73ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205490565b348015610d6e57600080fd5b506104a5610d7d3660046152d3565b600460209081526000928352604080842090915290825290205481565b348015610da657600080fd5b506105d4610db5366004614f38565b612f0f565b348015610dc657600080fd5b506104a5610dd5366004614f38565b612f1c565b348015610de657600080fd5b5061058a610df53660046154a5565b612f27565b348015610e0657600080fd5b5061058a610e1536600461503c565b612ff5565b6104a5610e2836600461503c565b6130c6565b348015610e3957600080fd5b506104a5613223565b6000610e4c6132f9565b9050610e56613223565b610e6090826155df565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610eed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1191906155f2565b610f1b908261560b565b905090565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610fb357507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60008054610fc69061561e565b80601f0160208091040260200160405190810160405280929190818152602001828054610ff29061561e565b801561103f5780601f106110145761010080835404028352916020019161103f565b820191906000526020600020905b81548152906001019060200180831161102257829003601f168201915b505050505081565b600254600090801561106b5761106661105e610e42565b84908361339e565b61106d565b825b9392505050565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906110dc9086815260200190565b60405180910390a350600192915050565b600254600090801561106b5761106681611105610e42565b8591906133da565b61111561341e565b604051602481018290526111e5907f7ec0689f00000000000000000000000000000000000000000000000000000000906044015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152600b5473ffffffffffffffffffffffffffffffffffffffff1690613432565b5050565b6111f1613457565b670de0b6b3a7640000811115611233576040517fc31c0b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a81905560405181815233907fb3a17cf8bbe3a4348266beede3a365af15dd59203021a6c1121d00ca2d5019f7906020015b60405180910390a250565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611305576112d383826155df565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff85166000908152600360205260408120805485929061133a9084906155df565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906113a29087815260200190565b60405180910390a3506001949350505050565b60006113c082611bc2565b6113cc57506000919050565b6113d7600c836134bf565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c9906024015b602060405180830381865afa158015611444573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb391906155f2565b61147061341e565b600061147d600c856134bf565b90506115208163371fd8e660e01b8560405160240161149e91815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526134cb565b61153f81632e1a7d4d60e01b8460405160240161149e91815260200190565b60408051858152602081018590529081018390527fcc438ae21763799a9f64a28c62b3f15f0b5da604b7ca35e17049605a6c59599f906060015b60405180910390a150505050565b6000828152600660205260409020600101546115a2816134eb565b6115ac83836134f5565b505050565b60007f000000000000000000000000000000000000000000000000000000000000000046146115e257610f1b6135e9565b507f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff811633146116b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6111e58282613683565b6000806116c8600c61373e565b90506000805b82811015611791576116e1600c82613749565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015290935073ffffffffffffffffffffffffffffffffffffffff84169150639b56d6c990602401602060405180830381865afa15801561174f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177391906155f2565b61177d908561560b565b9350806117898161566b565b9150506116ce565b50505090565b6000610fb382611047565b6117aa61341e565b6117b383613767565b6118548363e190febc60e01b84846040516024016117d29291906156ec565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526137a9565b6040518381527fed6771ea2f01816faa7ccf034b76201581d0b4374e86f83b06a8b4191c6b9f899060200160405180910390a1505050565b611894613457565b60008173ffffffffffffffffffffffffffffffffffffffff1663af640d0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118e1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061190591906155f2565b905061191081611bc2565b1561194a576040517f254de069000000000000000000000000000000000000000000000000000000008152600481018290526024016116a8565b611956600c82846137d4565b506040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8757b15b000000000000000000000000000000000000000000000000000000001790526119cf9073ffffffffffffffffffffffffffffffffffffffff841690613432565b506040805182815273ffffffffffffffffffffffffffffffffffffffff8416602082015233917f7ae8ce35a1794068c85a72f63d16209787d9e4f3e4b3095cf7f0e1de1e5e074191015b60405180910390a25050565b6000611a3083612f1c565b905080600003611a9c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f53484152455300000000000000000000000000000000000000000060448201526064016116a8565b611ade73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330866137ff565b611ae882826138be565b604080518481526020810183905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d791015b60405180910390a3610fb3565b611b49613457565b670de0b6b3a7640000811115611b8b576040517fc9034e1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881905560405181815233907f48d9d467a6699d3c9f09688328d42ebb56b0d1598687d06bebbe2e7f4264156290602001611266565b6000610fb3600c83613937565b611bd7613457565b601181905560405181815233907fb2bcb0a4720f48272ebaa5d990545b2ed974c8b87baec4514561aeedf05b9fc090602001611266565b611c16613943565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611ca0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc491906155f2565b841115611cfd576040517f2ca192fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83600f6000828254611d0f919061560b565b90915550611d28905083611d238385615700565b6139ab565b611d30613b72565b7f83387a3342ff1ebc5e437dc9ae0f98274afda12a11cf547eebec05a3e0b8f8a7611d596116bb565b611d61613223565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611deb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0f91906155f2565b60408051938452602084019290925290820152606001611579565b6000611e35836124f5565b9050611e7973ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330846137ff565b611e8382846138be565b604080518281526020810185905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79101611b34565b60018054610fc69061561e565b611ee8613457565b73ffffffffffffffffffffffffffffffffffffffff8116611f35576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405190815233907ffb7a5f1d35a7022d9d6343bfc9a25035829d0ea72da06978793c945b1d94a17f90602001611266565b611fb2613457565b611fbb82613767565b8015801561206357506000611fd1600c846134bf565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c990602401602060405180830381865afa15801561203d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206191906155f2565b115b1561209d576040517f254de069000000000000000000000000000000000000000000000000000000008152600481018390526024016116a8565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9265a7d5000000000000000000000000000000000000000000000000000000001790526120ff9083906137a9565b61210a600c83613c3f565b5060405182815233907fce27e7409a7e5bc40ecb15d4e938f30ecc5e1e44b03b8f9517eb0a3bf906eccc90602001611a19565b61214561341e565b670de0b6b3a7640000811115612187576040517fc31c0b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600090737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0906370a0823190602401602060405180830381865afa1580156121f2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221691906155f2565b905080831115612224578092505b6040517fde0e9a3e00000000000000000000000000000000000000000000000000000000815260048101849052600090737f39c581f595b53c5cb19bd0b3f8da6c935e2ca09063de0e9a3e906024016020604051808303816000875af1158015612292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122b691906155f2565b90506000612360847f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16631a612da6856040518263ffffffff1660e01b815260040161231991815260200190565b602060405180830381865afa158015612336573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061235a91906155f2565b90613c4b565b60405160248101849052604481018290529091506123a2907f0d3d0a640000000000000000000000000000000000000000000000000000000090606401611149565b505050505050565b6123b261341e565b60006123fa631f2913d260e01b877f00000000000000000000000000000000000000000000000000000000000000008886898960405160240161114996959493929190615774565b90507fd5a1cd88ddd329cc1ddf861a21708efa9ef3136b88e1420c21f91ef6f95ad02486868380602001905181019061243391906155f2565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401929092529082015260600160405180910390a1505050505050565b336000908152600360205260408120805483919083906124919084906155df565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906110dc9086815260200190565b600254600090801561106b5761106661250c610e42565b8490836133da565b600061251f846110ed565b90503373ffffffffffffffffffffffffffffffffffffffff8316146125d45773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146125d2576125a082826155df565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6125de8482613c60565b6125e88282613d45565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612675573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061269991906155f2565b9050808511156126a7578094505b604080518681526020810184905273ffffffffffffffffffffffffffffffffffffffff808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a461274073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168587613dd3565b509392505050565b60003373ffffffffffffffffffffffffffffffffffffffff8316146127fd5773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146127fb576127c985826155df565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b61280684611797565b905080600003612872576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f41535345545300000000000000000000000000000000000000000060448201526064016116a8565b61287c8185613c60565b6128868285613d45565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612913573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293791906155f2565b905080821115612945578091505b604080518381526020810187905273ffffffffffffffffffffffffffffffffffffffff808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a461274073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168584613dd3565b600254600090801561106b57611066816129f6610e42565b85919061339e565b612a0661341e565b6000612a13600c856134bf565b9050612a3481633540302360e01b8560405160240161149e91815260200190565b612a538163c5ebeaec60e01b8460405160240161149e91815260200190565b60408051858152602081018590529081018390527f9d9f44c86bd2cc36321fd3804a52754697914b32070b66b56b07270ef44fed1d90606001611579565b612a99613457565b670de0b6b3a7640000811115612adb576040517f7bfb537900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600981905560405181815233907fb414942c66de9d94d6f12b7818ecb6c326ee7f58980297cfba9a54b979e01d3390602001611266565b6000612b1d82611bc2565b612b2957506000919050565b612b34600c836134bf565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639a78e72e90602401611427565b612b90613943565b612b9981613e8c565b50565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260036020526040812054610fb390611047565b42841015612c35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064016116a8565b60006001612c416115b1565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612d93573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590612e0e57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b612e74576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e455200000000000000000000000000000000000060448201526064016116a8565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b600082815260066020526040902060010154612f05816134eb565b6115ac8383613683565b600061106d600c83614533565b6000610fb3826129de565b612f2f614542565b600081806020019051810190612f4591906157c6565b9050612f508161457e565b612fe673ba12222222228d8ba445958a75a0704d566bf2c884600081518110612f7b57612f7b6158b3565b602002602001015186600081518110612f9657612f966158b3565b6020026020010151612fa8919061560b565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169190613dd3565b612fee614603565b5050505050565b612ffd613457565b73ffffffffffffffffffffffffffffffffffffffff811661304a576040517f91f7acdb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010073ffffffffffffffffffffffffffffffffffffffff84169081029190911790915560405190815233907f4ab5be82436d353e61ca18726e984e561f5c1cc7c6d38b29d2553c790434705a90602001611266565b6000346130d281612f1c565b91508160000361313e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f53484152455300000000000000000000000000000000000000000060448201526064016116a8565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156131a657600080fd5b505af11580156131ba573d6000803e3d6000fd5b50505050506131c983836138be565b604080518281526020810184905273ffffffffffffffffffffffffffffffffffffffff85169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a35b50919050565b600080613230600c61373e565b90506000805b8281101561179157613249600c82613749565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015290935073ffffffffffffffffffffffffffffffffffffffff84169150639a78e72e90602401602060405180830381865afa1580156132b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132db91906155f2565b6132e5908561560b565b9350806132f18161566b565b915050613236565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16639023054a61333f6116bb565b6040518263ffffffff1660e01b815260040161335d91815260200190565b602060405180830381865afa15801561337a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1b91906155f2565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04841183021582026133d357600080fd5b5091020490565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048411830215820261340f57600080fd5b50910281810615159190040190565b60075460ff1661343057613430613943565b565b606061106d8383604051806060016040528060278152602001615c04602791396146fe565b3360009081527f54cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4f8602052604090205460ff16613430576040517f06d919f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061106d8383614783565b6115ac73ffffffffffffffffffffffffffffffffffffffff831682613432565b612b99813361480d565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166111e557600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561358b3390565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161361b91906158e2565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16156111e557600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000610fb3826148c7565b600080808061375886866148d2565b909450925050505b9250929050565b61377081611bc2565b612b99576040517f10028b54000000000000000000000000000000000000000000000000000000008152600481018290526024016116a8565b6115ac816137b8600c856134bf565b73ffffffffffffffffffffffffffffffffffffffff1690613432565b60006137f7848473ffffffffffffffffffffffffffffffffffffffff85166148fd565b949350505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080612fee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c454400000000000000000000000060448201526064016116a8565b80600260008282546138d0919061560b565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b600061106d838361491a565b3360009081527f102c8bff359ba7f792f3597c956360de34af36b3eee541f0c28549f821bb5369602052604090205460ff16613430576040517f04e1fa9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001808252818301909252600091602080830190803683370190505090507f000000000000000000000000000000000000000000000000000000000000000081600081518110613a0157613a016158b3565b73ffffffffffffffffffffffffffffffffffffffff92909216602092830291909101909101526040805160018082528183019092526000918160200160208202803683370190505090508381600081518110613a5f57613a5f6158b3565b602002602001018181525050613a9b600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b73ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16635c38449e30848487604051602001613adf91906159b8565b6040516020818303038152906040526040518563ffffffff1660e01b8152600401613b0d9493929190615a38565b600060405180830381600087803b158015613b2757600080fd5b505af1158015613b3b573d6000803e3d6000fd5b50505050613b6c600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b50505050565b600f546000613b7f613223565b613b876132f9565b613b9191906155df565b9050818111156111e557600f8190556000613bac83836155df565b90508060106000828254613bc0919061560b565b9091555050600854600090613bd6908390613c4b565b600754909150613c0990610100900473ffffffffffffffffffffffffffffffffffffffff16613c04836129de565b6138be565b60408051838152602081018390527ffa07446fad45314351eb89109a154880278451332bb87f1824d435fe58da59399101611579565b600061106d8383614926565b600061106d8383670de0b6b3a764000061339e565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015613ced573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d1191906155f2565b9050808311613d1f57505050565b60008160115485613d30919061560b565b613d3a91906155df565b9050613b6c81613e8c565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290613d7a9084906155df565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200161392b565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080613b6c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064016116a8565b6000613e98600c61373e565b9050600080613ea5613223565b613ead6132f9565b613eb791906155df565b90506000613ec684600161560b565b67ffffffffffffffff811115613ede57613ede615301565b604051908082528060200260200182016040528015613f1157816020015b6060815260200190600190039081613efc5790505b50905081851115613f20578194505b60008060008060005b888110156142b957613f3c600c82613749565b6040517f9b56d6c9000000000000000000000000000000000000000000000000000000008152306004820152919650945060009073ffffffffffffffffffffffffffffffffffffffff861690639b56d6c990602401602060405180830381865afa158015613fae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fd291906155f2565b905080600003613fe257506142a7565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff871690639a78e72e90602401602060405180830381865afa15801561404f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061407391906155f2565b90506000817f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16639023054a856040518263ffffffff1660e01b81526004016140d391815260200190565b602060405180830381865afa1580156140f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061411491906155f2565b61411e91906155df565b905061412b8d828c61339e565b945061413885838361339e565b9550614144868c61560b565b9a507f2e2ddd4500000000000000000000000000000000000000000000000000000000888773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663934e6a436141af8a8461560b565b6040518263ffffffff1660e01b81526004016141cd91815260200190565b602060405180830381865afa1580156141ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061420e91906155f2565b604051602481019390935260448301919091526064820152608401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050898581518110614298576142986158b3565b60200260200101819052505050505b806142b18161566b565b915050613f29565b5088600f60008282546142cc91906155df565b9091555050600a54604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248201526044808201939093528151808203909301835260640190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9e50767300000000000000000000000000000000000000000000000000000000179052855186908a908110614372576143726158b3565b60209081029190910101526040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561440a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061442e91906155f2565b905061443a88876139ab565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f7e472eae96e977766c8a0c6d0157161571f966cac5533f0acc3632d2cb8bf20f90829073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156144e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061450c91906155f2565b61451691906155df565b60405190815260200160405180910390a150505050505050505050565b60008080806137588686614943565b60075460ff16613430576040517fe17c49b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81518110156111e55781818151811061459c5761459c6158b3565b602002602001015151600003156145f1576145ef8282815181106145c2576145c26158b3565b60200260200101513073ffffffffffffffffffffffffffffffffffffffff1661343290919063ffffffff16565b505b806145fb8161566b565b915050614581565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015614690573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146b491906155f2565b601154909150808210156111e5576040517fcd62da4300000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044016116a8565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516147289190615aeb565b600060405180830381855af49150503d8060008114614763576040519150601f19603f3d011682016040523d82523d6000602084013e614768565b606091505b50915091506147798683838761497d565b9695505050505050565b6000818152600283016020526040812054801515806147a757506147a7848461491a565b61106d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b6579000060448201526064016116a8565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166111e55761484d81614a1d565b614858836020614a3c565b604051602001614869929190615b07565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a00000000000000000000000000000000000000000000000000000000082526116a891600401614f25565b6000610fb382614c7f565b600080806148e08585614c89565b600081815260029690960160205260409095205494959350505050565b600082815260028401602052604081208290556137f78484614c95565b600061106d8383614ca1565b6000818152600283016020526040812081905561106d8383614cb9565b600081815260028301602052604081205481908061497257614965858561491a565b9250600091506137609050565b600192509050613760565b60608315614a13578251600003614a0c5773ffffffffffffffffffffffffffffffffffffffff85163b614a0c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016116a8565b50816137f7565b6137f78383614cc5565b6060610fb373ffffffffffffffffffffffffffffffffffffffff831660145b60606000614a4b836002615b88565b614a5690600261560b565b67ffffffffffffffff811115614a6e57614a6e615301565b6040519080825280601f01601f191660200182016040528015614a98576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110614acf57614acf6158b3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110614b3257614b326158b3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000614b6e846002615b88565b614b7990600161560b565b90505b6001811115614c16577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110614bba57614bba6158b3565b1a60f81b828281518110614bd057614bd06158b3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93614c0f81615b9f565b9050614b7c565b50831561106d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016116a8565b6000610fb3825490565b600061106d8383614d09565b600061106d8383614d33565b6000818152600183016020526040812054151561106d565b600061106d8383614d82565b815115614cd55781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116a89190614f25565b6000826000018281548110614d2057614d206158b3565b9060005260206000200154905092915050565b6000818152600183016020526040812054614d7a57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610fb3565b506000610fb3565b60008181526001830160205260408120548015614e6b576000614da66001836155df565b8554909150600090614dba906001906155df565b9050818114614e1f576000866000018281548110614dda57614dda6158b3565b9060005260206000200154905080876000018481548110614dfd57614dfd6158b3565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614e3057614e30615bd4565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610fb3565b6000915050610fb3565b600060208284031215614e8757600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461106d57600080fd5b60005b83811015614ed2578181015183820152602001614eba565b50506000910152565b60008151808452614ef3816020860160208601614eb7565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061106d6020830184614edb565b600060208284031215614f4a57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114612b9957600080fd5b60008060408385031215614f8657600080fd5b8235614f9181614f51565b946020939093013593505050565b600080600060608486031215614fb457600080fd5b8335614fbf81614f51565b92506020840135614fcf81614f51565b929592945050506040919091013590565b600080600060608486031215614ff557600080fd5b505081359360208301359350604090920135919050565b6000806040838503121561501f57600080fd5b82359150602083013561503181614f51565b809150509250929050565b60006020828403121561504e57600080fd5b813561106d81614f51565b60008083601f84011261506b57600080fd5b50813567ffffffffffffffff81111561508357600080fd5b60208301915083602082850101111561376057600080fd5b6000806000604084860312156150b057600080fd5b83359250602084013567ffffffffffffffff8111156150ce57600080fd5b6150da86828701615059565b9497909650939450505050565b600080600080606085870312156150fd57600080fd5b8435935060208501359250604085013567ffffffffffffffff8082111561512357600080fd5b818701915087601f83011261513757600080fd5b81358181111561514657600080fd5b8860208260051b850101111561515b57600080fd5b95989497505060200194505050565b6000806040838503121561517d57600080fd5b823591506020830135801515811461503157600080fd5b600080604083850312156151a757600080fd5b50508035926020909101359150565b6000806000806000608086880312156151ce57600080fd5b85356151d981614f51565b945060208601359350604086013567ffffffffffffffff8111156151fc57600080fd5b61520888828901615059565b96999598509660600135949350505050565b60008060006060848603121561522f57600080fd5b83359250602084013561524181614f51565b9150604084013561525181614f51565b809150509250925092565b600080600080600080600060e0888a03121561527757600080fd5b873561528281614f51565b9650602088013561529281614f51565b95506040880135945060608801359350608088013560ff811681146152b657600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156152e657600080fd5b82356152f181614f51565b9150602083013561503181614f51565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561537757615377615301565b604052919050565b600067ffffffffffffffff82111561539957615399615301565b5060051b60200190565b600082601f8301126153b457600080fd5b813560206153c96153c48361537f565b615330565b82815260059290921b840181019181810190868411156153e857600080fd5b8286015b8481101561540357803583529183019183016153ec565b509695505050505050565b600067ffffffffffffffff82111561542857615428615301565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f83011261546557600080fd5b81356154736153c48261540e565b81815284602083860101111561548857600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080608085870312156154bb57600080fd5b843567ffffffffffffffff808211156154d357600080fd5b818701915087601f8301126154e757600080fd5b813560206154f76153c48361537f565b82815260059290921b8401810191818101908b84111561551657600080fd5b948201945b8386101561553d57853561552e81614f51565b8252948201949082019061551b565b9850508801359250508082111561555357600080fd5b61555f888389016153a3565b9450604087013591508082111561557557600080fd5b615581888389016153a3565b9350606087013591508082111561559757600080fd5b506155a487828801615454565b91505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610fb357610fb36155b0565b60006020828403121561560457600080fd5b5051919050565b80820180821115610fb357610fb36155b0565b600181811c9082168061563257607f821691505b60208210810361321d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361569c5761569c6155b0565b5060010190565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6020815260006137f76020830184866156a3565b600061570e6153c48461537f565b80848252602080830192508560051b85013681111561572c57600080fd5b855b8181101561576857803567ffffffffffffffff81111561574e5760008081fd5b61575a36828a01615454565b86525093820193820161572e565b50919695505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a060808301526157ba60a0830184866156a3565b98975050505050505050565b600060208083850312156157d957600080fd5b825167ffffffffffffffff808211156157f157600080fd5b818501915085601f83011261580557600080fd5b81516158136153c48261537f565b81815260059190911b8301840190848101908883111561583257600080fd5b8585015b838110156158a65780518581111561584e5760008081fd5b8601603f81018b136158605760008081fd5b8781015160406158726153c48361540e565b8281528d828486010111156158875760008081fd5b615896838c8301848701614eb7565b8652505050918601918601615836565b5098975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080835481600182811c9150808316806158fe57607f831692505b60208084108203615936577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b81801561594a576001811461597d576159aa565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528415158502890196506159aa565b60008a81526020902060005b868110156159a25781548b820152908501908301615989565b505084890196505b509498975050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015615a2b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452615a19858351614edb565b945092850192908501906001016159df565b5092979650505050505050565b60006080820173ffffffffffffffffffffffffffffffffffffffff8088168452602060808186015282885180855260a087019150828a01945060005b81811015615a92578551851683529483019491830191600101615a74565b5050858103604087015287518082529082019350915080870160005b83811015615aca57815185529382019390820190600101615aae565b505050508281036060840152615ae08185614edb565b979650505050505050565b60008251615afd818460208701614eb7565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615b3f816017850160208801614eb7565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351615b7c816028840160208801614eb7565b01602801949350505050565b8082028115828204841417610fb357610fb36155b0565b600081615bae57615bae6155b0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122071f10f1d018986459eb939b84fda173cf15afea0db64873f021917aae20202f964736f6c6343000815003300000000000000000000000084f67f75daf6d57aef500e0c85c77b7b3bbc92a900000000000000000000000006444b9f0c6a966b8b9bc1e808d2b165a87e3a38000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006649f12b5ef495a3861b21e3206b1abfa33a6531000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca

Deployed Bytecode

0x6080604052600436106104845760003560e01c8063877887821161025e578063c63d75b611610143578063d547741f116100bb578063ef8b30f71161008a578063f0f442601161006f578063f0f4426014610dfa578063f340fa0114610e1a578063fc7b9c1814610e2d57600080fd5b8063ef8b30f714610dba578063f04f270714610dda57600080fd5b8063d547741f14610cff578063d905777e14610d1f578063dd62ed3e14610d62578063ddeadbb614610d9a57600080fd5b8063c9dd1f3c11610112578063ce96cb77116100f7578063ce96cb7714610ca9578063d03153aa14610cc9578063d505accf14610cdf57600080fd5b8063c9dd1f3c14610c69578063ce8c42e814610c8957600080fd5b8063c63d75b6146107cf578063c6e6f59214610c09578063c8b2877614610c29578063c923299a14610c4957600080fd5b80639e507673116101d6578063b3d7f6b9116101a5578063b93855101161018a578063b938551014610b9f578063ba08765214610bd3578063c245823414610bf357600080fd5b8063b3d7f6b914610b5f578063b460af9414610b7f57600080fd5b80639e50767314610aea578063a217fddf14610b0a578063a59dffd514610b1f578063a9059cbb14610b3f57600080fd5b80639356966d1161022d57806395d89b411161021257806395d89b4114610a955780639c82f2a414610aaa5780639ceffcde14610aca57600080fd5b80639356966d14610a5557806394bf804d14610a7557600080fd5b806387788782146109ac5780638d60cded146109c2578063902d14a0146109e257806391d1485414610a0257600080fd5b80633644e515116103845780635892457d116102fc5780636e553f65116102cb57806370a08231116102b057806370a082311461093c5780637ecebe0014610969578063860015191461099657600080fd5b80636e553f65146108fc57806370897b231461091c57600080fd5b80635892457d1461087a57806360d54d411461089057806361d027b3146108b05780636457f755146108e257600080fd5b8063402d267d116103535780634cdad506116103385780634cdad506146108245780635216aeec14610844578063549dd8c31461085a57600080fd5b8063402d267d146107cf5780634ac8eb5f1461080f57600080fd5b80633644e51514610732578063364bc15a1461074757806336568abe1461077b57806338d52e0f1461079b57600080fd5b8063158274a5116104175780632a62a490116103e65780632e2ddd45116103cb5780632e2ddd45146106ac5780632f2ff15d146106cc578063313ce567146106ec57600080fd5b80632a62a4901461065f5780632b3297f91461067f57600080fd5b8063158274a5146105ac57806318160ddd146105f957806323b872dd1461060f578063248a9ca31461062f57600080fd5b8063095ea7b311610453578063095ea7b31461052a5780630a28a4771461054a5780630e567c811461056a578063117da1ee1461058c57600080fd5b806301e1d1141461049057806301ffc9a7146104b857806306fdde03146104e857806307a2d13a1461050a57600080fd5b3661048b57005b600080fd5b34801561049c57600080fd5b506104a5610e42565b6040519081526020015b60405180910390f35b3480156104c457600080fd5b506104d86104d3366004614e75565b610f20565b60405190151581526020016104af565b3480156104f457600080fd5b506104fd610fb9565b6040516104af9190614f25565b34801561051657600080fd5b506104a5610525366004614f38565b611047565b34801561053657600080fd5b506104d8610545366004614f73565b611074565b34801561055657600080fd5b506104a5610565366004614f38565b6110ed565b34801561057657600080fd5b5061058a610585366004614f38565b61110d565b005b34801561059857600080fd5b5061058a6105a7366004614f38565b6111e9565b3480156105b857600080fd5b506105d473ba12222222228d8ba445958a75a0704d566bf2c881565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016104af565b34801561060557600080fd5b506104a560025481565b34801561061b57600080fd5b506104d861062a366004614f9f565b611271565b34801561063b57600080fd5b506104a561064a366004614f38565b60009081526006602052604090206001015490565b34801561066b57600080fd5b506104a561067a366004614f38565b6113b5565b34801561068b57600080fd5b50600b546105d49073ffffffffffffffffffffffffffffffffffffffff1681565b3480156106b857600080fd5b5061058a6106c7366004614fe0565b611468565b3480156106d857600080fd5b5061058a6106e736600461500c565b611587565b3480156106f857600080fd5b506107207f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff90911681526020016104af565b34801561073e57600080fd5b506104a56115b1565b34801561075357600080fd5b506104a57ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b34801561078757600080fd5b5061058a61079636600461500c565b611607565b3480156107a757600080fd5b506105d47f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b3480156107db57600080fd5b506104a56107ea36600461503c565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b34801561081b57600080fd5b506104a56116bb565b34801561083057600080fd5b506104a561083f366004614f38565b611797565b34801561085057600080fd5b506104a5600f5481565b34801561086657600080fd5b5061058a61087536600461509b565b6117a2565b34801561088657600080fd5b506104a560095481565b34801561089c57600080fd5b5061058a6108ab36600461503c565b61188c565b3480156108bc57600080fd5b506007546105d490610100900473ffffffffffffffffffffffffffffffffffffffff1681565b3480156108ee57600080fd5b506007546104d89060ff1681565b34801561090857600080fd5b506104a561091736600461500c565b611a25565b34801561092857600080fd5b5061058a610937366004614f38565b611b41565b34801561094857600080fd5b506104a561095736600461503c565b60036020526000908152604090205481565b34801561097557600080fd5b506104a561098436600461503c565b60056020526000908152604090205481565b3480156109a257600080fd5b506104a560105481565b3480156109b857600080fd5b506104a560085481565b3480156109ce57600080fd5b506104d86109dd366004614f38565b611bc2565b3480156109ee57600080fd5b5061058a6109fd366004614f38565b611bcf565b348015610a0e57600080fd5b506104d8610a1d36600461500c565b600091825260066020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b348015610a6157600080fd5b5061058a610a703660046150e7565b611c0e565b348015610a8157600080fd5b506104a5610a9036600461500c565b611e2a565b348015610aa157600080fd5b506104fd611ed3565b348015610ab657600080fd5b5061058a610ac536600461503c565b611ee0565b348015610ad657600080fd5b5061058a610ae536600461516a565b611faa565b348015610af657600080fd5b5061058a610b05366004615194565b61213d565b348015610b1657600080fd5b506104a5600081565b348015610b2b57600080fd5b5061058a610b3a3660046151b6565b6123aa565b348015610b4b57600080fd5b506104d8610b5a366004614f73565b612470565b348015610b6b57600080fd5b506104a5610b7a366004614f38565b6124f5565b348015610b8b57600080fd5b506104a5610b9a36600461521a565b612514565b348015610bab57600080fd5b506105d47f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca81565b348015610bdf57600080fd5b506104a5610bee36600461521a565b612748565b348015610bff57600080fd5b506104a560115481565b348015610c1557600080fd5b506104a5610c24366004614f38565b6129de565b348015610c3557600080fd5b5061058a610c44366004614fe0565b6129fe565b348015610c5557600080fd5b5061058a610c64366004614f38565b612a91565b348015610c7557600080fd5b506104a5610c84366004614f38565b612b12565b348015610c9557600080fd5b5061058a610ca4366004614f38565b612b88565b348015610cb557600080fd5b506104a5610cc436600461503c565b612b9c565b348015610cd557600080fd5b506104a5600a5481565b348015610ceb57600080fd5b5061058a610cfa36600461525c565b612bcb565b348015610d0b57600080fd5b5061058a610d1a36600461500c565b612eea565b348015610d2b57600080fd5b506104a5610d3a36600461503c565b73ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205490565b348015610d6e57600080fd5b506104a5610d7d3660046152d3565b600460209081526000928352604080842090915290825290205481565b348015610da657600080fd5b506105d4610db5366004614f38565b612f0f565b348015610dc657600080fd5b506104a5610dd5366004614f38565b612f1c565b348015610de657600080fd5b5061058a610df53660046154a5565b612f27565b348015610e0657600080fd5b5061058a610e1536600461503c565b612ff5565b6104a5610e2836600461503c565b6130c6565b348015610e3957600080fd5b506104a5613223565b6000610e4c6132f9565b9050610e56613223565b610e6090826155df565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610eed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1191906155f2565b610f1b908261560b565b905090565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610fb357507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60008054610fc69061561e565b80601f0160208091040260200160405190810160405280929190818152602001828054610ff29061561e565b801561103f5780601f106110145761010080835404028352916020019161103f565b820191906000526020600020905b81548152906001019060200180831161102257829003601f168201915b505050505081565b600254600090801561106b5761106661105e610e42565b84908361339e565b61106d565b825b9392505050565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906110dc9086815260200190565b60405180910390a350600192915050565b600254600090801561106b5761106681611105610e42565b8591906133da565b61111561341e565b604051602481018290526111e5907f7ec0689f00000000000000000000000000000000000000000000000000000000906044015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152600b5473ffffffffffffffffffffffffffffffffffffffff1690613432565b5050565b6111f1613457565b670de0b6b3a7640000811115611233576040517fc31c0b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a81905560405181815233907fb3a17cf8bbe3a4348266beede3a365af15dd59203021a6c1121d00ca2d5019f7906020015b60405180910390a250565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611305576112d383826155df565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff85166000908152600360205260408120805485929061133a9084906155df565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906113a29087815260200190565b60405180910390a3506001949350505050565b60006113c082611bc2565b6113cc57506000919050565b6113d7600c836134bf565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c9906024015b602060405180830381865afa158015611444573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb391906155f2565b61147061341e565b600061147d600c856134bf565b90506115208163371fd8e660e01b8560405160240161149e91815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526134cb565b61153f81632e1a7d4d60e01b8460405160240161149e91815260200190565b60408051858152602081018590529081018390527fcc438ae21763799a9f64a28c62b3f15f0b5da604b7ca35e17049605a6c59599f906060015b60405180910390a150505050565b6000828152600660205260409020600101546115a2816134eb565b6115ac83836134f5565b505050565b60007f000000000000000000000000000000000000000000000000000000000000000146146115e257610f1b6135e9565b507faf4e01229ea2663d2a0162310c0ba1a1f02c20dd81702962a3259078fc919b7490565b73ffffffffffffffffffffffffffffffffffffffff811633146116b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6111e58282613683565b6000806116c8600c61373e565b90506000805b82811015611791576116e1600c82613749565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015290935073ffffffffffffffffffffffffffffffffffffffff84169150639b56d6c990602401602060405180830381865afa15801561174f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177391906155f2565b61177d908561560b565b9350806117898161566b565b9150506116ce565b50505090565b6000610fb382611047565b6117aa61341e565b6117b383613767565b6118548363e190febc60e01b84846040516024016117d29291906156ec565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526137a9565b6040518381527fed6771ea2f01816faa7ccf034b76201581d0b4374e86f83b06a8b4191c6b9f899060200160405180910390a1505050565b611894613457565b60008173ffffffffffffffffffffffffffffffffffffffff1663af640d0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118e1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061190591906155f2565b905061191081611bc2565b1561194a576040517f254de069000000000000000000000000000000000000000000000000000000008152600481018290526024016116a8565b611956600c82846137d4565b506040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8757b15b000000000000000000000000000000000000000000000000000000001790526119cf9073ffffffffffffffffffffffffffffffffffffffff841690613432565b506040805182815273ffffffffffffffffffffffffffffffffffffffff8416602082015233917f7ae8ce35a1794068c85a72f63d16209787d9e4f3e4b3095cf7f0e1de1e5e074191015b60405180910390a25050565b6000611a3083612f1c565b905080600003611a9c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f53484152455300000000000000000000000000000000000000000060448201526064016116a8565b611ade73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2163330866137ff565b611ae882826138be565b604080518481526020810183905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d791015b60405180910390a3610fb3565b611b49613457565b670de0b6b3a7640000811115611b8b576040517fc9034e1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881905560405181815233907f48d9d467a6699d3c9f09688328d42ebb56b0d1598687d06bebbe2e7f4264156290602001611266565b6000610fb3600c83613937565b611bd7613457565b601181905560405181815233907fb2bcb0a4720f48272ebaa5d990545b2ed974c8b87baec4514561aeedf05b9fc090602001611266565b611c16613943565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611ca0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc491906155f2565b841115611cfd576040517f2ca192fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83600f6000828254611d0f919061560b565b90915550611d28905083611d238385615700565b6139ab565b611d30613b72565b7f83387a3342ff1ebc5e437dc9ae0f98274afda12a11cf547eebec05a3e0b8f8a7611d596116bb565b611d61613223565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611deb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0f91906155f2565b60408051938452602084019290925290820152606001611579565b6000611e35836124f5565b9050611e7973ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2163330846137ff565b611e8382846138be565b604080518281526020810185905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79101611b34565b60018054610fc69061561e565b611ee8613457565b73ffffffffffffffffffffffffffffffffffffffff8116611f35576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405190815233907ffb7a5f1d35a7022d9d6343bfc9a25035829d0ea72da06978793c945b1d94a17f90602001611266565b611fb2613457565b611fbb82613767565b8015801561206357506000611fd1600c846134bf565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c990602401602060405180830381865afa15801561203d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206191906155f2565b115b1561209d576040517f254de069000000000000000000000000000000000000000000000000000000008152600481018390526024016116a8565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9265a7d5000000000000000000000000000000000000000000000000000000001790526120ff9083906137a9565b61210a600c83613c3f565b5060405182815233907fce27e7409a7e5bc40ecb15d4e938f30ecc5e1e44b03b8f9517eb0a3bf906eccc90602001611a19565b61214561341e565b670de0b6b3a7640000811115612187576040517fc31c0b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600090737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0906370a0823190602401602060405180830381865afa1580156121f2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221691906155f2565b905080831115612224578092505b6040517fde0e9a3e00000000000000000000000000000000000000000000000000000000815260048101849052600090737f39c581f595b53c5cb19bd0b3f8da6c935e2ca09063de0e9a3e906024016020604051808303816000875af1158015612292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122b691906155f2565b90506000612360847f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca73ffffffffffffffffffffffffffffffffffffffff16631a612da6856040518263ffffffff1660e01b815260040161231991815260200190565b602060405180830381865afa158015612336573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061235a91906155f2565b90613c4b565b60405160248101849052604481018290529091506123a2907f0d3d0a640000000000000000000000000000000000000000000000000000000090606401611149565b505050505050565b6123b261341e565b60006123fa631f2913d260e01b877f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28886898960405160240161114996959493929190615774565b90507fd5a1cd88ddd329cc1ddf861a21708efa9ef3136b88e1420c21f91ef6f95ad02486868380602001905181019061243391906155f2565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401929092529082015260600160405180910390a1505050505050565b336000908152600360205260408120805483919083906124919084906155df565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906110dc9086815260200190565b600254600090801561106b5761106661250c610e42565b8490836133da565b600061251f846110ed565b90503373ffffffffffffffffffffffffffffffffffffffff8316146125d45773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146125d2576125a082826155df565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6125de8482613c60565b6125e88282613d45565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612675573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061269991906155f2565b9050808511156126a7578094505b604080518681526020810184905273ffffffffffffffffffffffffffffffffffffffff808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a461274073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168587613dd3565b509392505050565b60003373ffffffffffffffffffffffffffffffffffffffff8316146127fd5773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146127fb576127c985826155df565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b61280684611797565b905080600003612872576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f41535345545300000000000000000000000000000000000000000060448201526064016116a8565b61287c8185613c60565b6128868285613d45565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612913573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293791906155f2565b905080821115612945578091505b604080518381526020810187905273ffffffffffffffffffffffffffffffffffffffff808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a461274073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168584613dd3565b600254600090801561106b57611066816129f6610e42565b85919061339e565b612a0661341e565b6000612a13600c856134bf565b9050612a3481633540302360e01b8560405160240161149e91815260200190565b612a538163c5ebeaec60e01b8460405160240161149e91815260200190565b60408051858152602081018590529081018390527f9d9f44c86bd2cc36321fd3804a52754697914b32070b66b56b07270ef44fed1d90606001611579565b612a99613457565b670de0b6b3a7640000811115612adb576040517f7bfb537900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600981905560405181815233907fb414942c66de9d94d6f12b7818ecb6c326ee7f58980297cfba9a54b979e01d3390602001611266565b6000612b1d82611bc2565b612b2957506000919050565b612b34600c836134bf565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639a78e72e90602401611427565b612b90613943565b612b9981613e8c565b50565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260036020526040812054610fb390611047565b42841015612c35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064016116a8565b60006001612c416115b1565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612d93573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590612e0e57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b612e74576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e455200000000000000000000000000000000000060448201526064016116a8565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b600082815260066020526040902060010154612f05816134eb565b6115ac8383613683565b600061106d600c83614533565b6000610fb3826129de565b612f2f614542565b600081806020019051810190612f4591906157c6565b9050612f508161457e565b612fe673ba12222222228d8ba445958a75a0704d566bf2c884600081518110612f7b57612f7b6158b3565b602002602001015186600081518110612f9657612f966158b3565b6020026020010151612fa8919061560b565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169190613dd3565b612fee614603565b5050505050565b612ffd613457565b73ffffffffffffffffffffffffffffffffffffffff811661304a576040517f91f7acdb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010073ffffffffffffffffffffffffffffffffffffffff84169081029190911790915560405190815233907f4ab5be82436d353e61ca18726e984e561f5c1cc7c6d38b29d2553c790434705a90602001611266565b6000346130d281612f1c565b91508160000361313e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f53484152455300000000000000000000000000000000000000000060448201526064016116a8565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156131a657600080fd5b505af11580156131ba573d6000803e3d6000fd5b50505050506131c983836138be565b604080518281526020810184905273ffffffffffffffffffffffffffffffffffffffff85169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a35b50919050565b600080613230600c61373e565b90506000805b8281101561179157613249600c82613749565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015290935073ffffffffffffffffffffffffffffffffffffffff84169150639a78e72e90602401602060405180830381865afa1580156132b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132db91906155f2565b6132e5908561560b565b9350806132f18161566b565b915050613236565b60007f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca73ffffffffffffffffffffffffffffffffffffffff16639023054a61333f6116bb565b6040518263ffffffff1660e01b815260040161335d91815260200190565b602060405180830381865afa15801561337a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1b91906155f2565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04841183021582026133d357600080fd5b5091020490565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048411830215820261340f57600080fd5b50910281810615159190040190565b60075460ff1661343057613430613943565b565b606061106d8383604051806060016040528060278152602001615c04602791396146fe565b3360009081527f54cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4f8602052604090205460ff16613430576040517f06d919f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061106d8383614783565b6115ac73ffffffffffffffffffffffffffffffffffffffff831682613432565b612b99813361480d565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166111e557600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561358b3390565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161361b91906158e2565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16156111e557600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000610fb3826148c7565b600080808061375886866148d2565b909450925050505b9250929050565b61377081611bc2565b612b99576040517f10028b54000000000000000000000000000000000000000000000000000000008152600481018290526024016116a8565b6115ac816137b8600c856134bf565b73ffffffffffffffffffffffffffffffffffffffff1690613432565b60006137f7848473ffffffffffffffffffffffffffffffffffffffff85166148fd565b949350505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080612fee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c454400000000000000000000000060448201526064016116a8565b80600260008282546138d0919061560b565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b600061106d838361491a565b3360009081527f102c8bff359ba7f792f3597c956360de34af36b3eee541f0c28549f821bb5369602052604090205460ff16613430576040517f04e1fa9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001808252818301909252600091602080830190803683370190505090507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281600081518110613a0157613a016158b3565b73ffffffffffffffffffffffffffffffffffffffff92909216602092830291909101909101526040805160018082528183019092526000918160200160208202803683370190505090508381600081518110613a5f57613a5f6158b3565b602002602001018181525050613a9b600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b73ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16635c38449e30848487604051602001613adf91906159b8565b6040516020818303038152906040526040518563ffffffff1660e01b8152600401613b0d9493929190615a38565b600060405180830381600087803b158015613b2757600080fd5b505af1158015613b3b573d6000803e3d6000fd5b50505050613b6c600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b50505050565b600f546000613b7f613223565b613b876132f9565b613b9191906155df565b9050818111156111e557600f8190556000613bac83836155df565b90508060106000828254613bc0919061560b565b9091555050600854600090613bd6908390613c4b565b600754909150613c0990610100900473ffffffffffffffffffffffffffffffffffffffff16613c04836129de565b6138be565b60408051838152602081018390527ffa07446fad45314351eb89109a154880278451332bb87f1824d435fe58da59399101611579565b600061106d8383614926565b600061106d8383670de0b6b3a764000061339e565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015613ced573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d1191906155f2565b9050808311613d1f57505050565b60008160115485613d30919061560b565b613d3a91906155df565b9050613b6c81613e8c565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290613d7a9084906155df565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200161392b565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080613b6c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064016116a8565b6000613e98600c61373e565b9050600080613ea5613223565b613ead6132f9565b613eb791906155df565b90506000613ec684600161560b565b67ffffffffffffffff811115613ede57613ede615301565b604051908082528060200260200182016040528015613f1157816020015b6060815260200190600190039081613efc5790505b50905081851115613f20578194505b60008060008060005b888110156142b957613f3c600c82613749565b6040517f9b56d6c9000000000000000000000000000000000000000000000000000000008152306004820152919650945060009073ffffffffffffffffffffffffffffffffffffffff861690639b56d6c990602401602060405180830381865afa158015613fae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fd291906155f2565b905080600003613fe257506142a7565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff871690639a78e72e90602401602060405180830381865afa15801561404f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061407391906155f2565b90506000817f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca73ffffffffffffffffffffffffffffffffffffffff16639023054a856040518263ffffffff1660e01b81526004016140d391815260200190565b602060405180830381865afa1580156140f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061411491906155f2565b61411e91906155df565b905061412b8d828c61339e565b945061413885838361339e565b9550614144868c61560b565b9a507f2e2ddd4500000000000000000000000000000000000000000000000000000000888773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca1663934e6a436141af8a8461560b565b6040518263ffffffff1660e01b81526004016141cd91815260200190565b602060405180830381865afa1580156141ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061420e91906155f2565b604051602481019390935260448301919091526064820152608401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050898581518110614298576142986158b3565b60200260200101819052505050505b806142b18161566b565b915050613f29565b5088600f60008282546142cc91906155df565b9091555050600a54604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248201526044808201939093528151808203909301835260640190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9e50767300000000000000000000000000000000000000000000000000000000179052855186908a908110614372576143726158b3565b60209081029190910101526040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa15801561440a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061442e91906155f2565b905061443a88876139ab565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f7e472eae96e977766c8a0c6d0157161571f966cac5533f0acc3632d2cb8bf20f90829073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa1580156144e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061450c91906155f2565b61451691906155df565b60405190815260200160405180910390a150505050505050505050565b60008080806137588686614943565b60075460ff16613430576040517fe17c49b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81518110156111e55781818151811061459c5761459c6158b3565b602002602001015151600003156145f1576145ef8282815181106145c2576145c26158b3565b60200260200101513073ffffffffffffffffffffffffffffffffffffffff1661343290919063ffffffff16565b505b806145fb8161566b565b915050614581565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015614690573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146b491906155f2565b601154909150808210156111e5576040517fcd62da4300000000000000000000000000000000000000000000000000000000815260048101839052602481018290526044016116a8565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516147289190615aeb565b600060405180830381855af49150503d8060008114614763576040519150601f19603f3d011682016040523d82523d6000602084013e614768565b606091505b50915091506147798683838761497d565b9695505050505050565b6000818152600283016020526040812054801515806147a757506147a7848461491a565b61106d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b6579000060448201526064016116a8565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166111e55761484d81614a1d565b614858836020614a3c565b604051602001614869929190615b07565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a00000000000000000000000000000000000000000000000000000000082526116a891600401614f25565b6000610fb382614c7f565b600080806148e08585614c89565b600081815260029690960160205260409095205494959350505050565b600082815260028401602052604081208290556137f78484614c95565b600061106d8383614ca1565b6000818152600283016020526040812081905561106d8383614cb9565b600081815260028301602052604081205481908061497257614965858561491a565b9250600091506137609050565b600192509050613760565b60608315614a13578251600003614a0c5773ffffffffffffffffffffffffffffffffffffffff85163b614a0c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016116a8565b50816137f7565b6137f78383614cc5565b6060610fb373ffffffffffffffffffffffffffffffffffffffff831660145b60606000614a4b836002615b88565b614a5690600261560b565b67ffffffffffffffff811115614a6e57614a6e615301565b6040519080825280601f01601f191660200182016040528015614a98576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110614acf57614acf6158b3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110614b3257614b326158b3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000614b6e846002615b88565b614b7990600161560b565b90505b6001811115614c16577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110614bba57614bba6158b3565b1a60f81b828281518110614bd057614bd06158b3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93614c0f81615b9f565b9050614b7c565b50831561106d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016116a8565b6000610fb3825490565b600061106d8383614d09565b600061106d8383614d33565b6000818152600183016020526040812054151561106d565b600061106d8383614d82565b815115614cd55781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116a89190614f25565b6000826000018281548110614d2057614d206158b3565b9060005260206000200154905092915050565b6000818152600183016020526040812054614d7a57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610fb3565b506000610fb3565b60008181526001830160205260408120548015614e6b576000614da66001836155df565b8554909150600090614dba906001906155df565b9050818114614e1f576000866000018281548110614dda57614dda6158b3565b9060005260206000200154905080876000018481548110614dfd57614dfd6158b3565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614e3057614e30615bd4565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610fb3565b6000915050610fb3565b600060208284031215614e8757600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461106d57600080fd5b60005b83811015614ed2578181015183820152602001614eba565b50506000910152565b60008151808452614ef3816020860160208601614eb7565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061106d6020830184614edb565b600060208284031215614f4a57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114612b9957600080fd5b60008060408385031215614f8657600080fd5b8235614f9181614f51565b946020939093013593505050565b600080600060608486031215614fb457600080fd5b8335614fbf81614f51565b92506020840135614fcf81614f51565b929592945050506040919091013590565b600080600060608486031215614ff557600080fd5b505081359360208301359350604090920135919050565b6000806040838503121561501f57600080fd5b82359150602083013561503181614f51565b809150509250929050565b60006020828403121561504e57600080fd5b813561106d81614f51565b60008083601f84011261506b57600080fd5b50813567ffffffffffffffff81111561508357600080fd5b60208301915083602082850101111561376057600080fd5b6000806000604084860312156150b057600080fd5b83359250602084013567ffffffffffffffff8111156150ce57600080fd5b6150da86828701615059565b9497909650939450505050565b600080600080606085870312156150fd57600080fd5b8435935060208501359250604085013567ffffffffffffffff8082111561512357600080fd5b818701915087601f83011261513757600080fd5b81358181111561514657600080fd5b8860208260051b850101111561515b57600080fd5b95989497505060200194505050565b6000806040838503121561517d57600080fd5b823591506020830135801515811461503157600080fd5b600080604083850312156151a757600080fd5b50508035926020909101359150565b6000806000806000608086880312156151ce57600080fd5b85356151d981614f51565b945060208601359350604086013567ffffffffffffffff8111156151fc57600080fd5b61520888828901615059565b96999598509660600135949350505050565b60008060006060848603121561522f57600080fd5b83359250602084013561524181614f51565b9150604084013561525181614f51565b809150509250925092565b600080600080600080600060e0888a03121561527757600080fd5b873561528281614f51565b9650602088013561529281614f51565b95506040880135945060608801359350608088013560ff811681146152b657600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156152e657600080fd5b82356152f181614f51565b9150602083013561503181614f51565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561537757615377615301565b604052919050565b600067ffffffffffffffff82111561539957615399615301565b5060051b60200190565b600082601f8301126153b457600080fd5b813560206153c96153c48361537f565b615330565b82815260059290921b840181019181810190868411156153e857600080fd5b8286015b8481101561540357803583529183019183016153ec565b509695505050505050565b600067ffffffffffffffff82111561542857615428615301565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f83011261546557600080fd5b81356154736153c48261540e565b81815284602083860101111561548857600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080608085870312156154bb57600080fd5b843567ffffffffffffffff808211156154d357600080fd5b818701915087601f8301126154e757600080fd5b813560206154f76153c48361537f565b82815260059290921b8401810191818101908b84111561551657600080fd5b948201945b8386101561553d57853561552e81614f51565b8252948201949082019061551b565b9850508801359250508082111561555357600080fd5b61555f888389016153a3565b9450604087013591508082111561557557600080fd5b615581888389016153a3565b9350606087013591508082111561559757600080fd5b506155a487828801615454565b91505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610fb357610fb36155b0565b60006020828403121561560457600080fd5b5051919050565b80820180821115610fb357610fb36155b0565b600181811c9082168061563257607f821691505b60208210810361321d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361569c5761569c6155b0565b5060010190565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6020815260006137f76020830184866156a3565b600061570e6153c48461537f565b80848252602080830192508560051b85013681111561572c57600080fd5b855b8181101561576857803567ffffffffffffffff81111561574e5760008081fd5b61575a36828a01615454565b86525093820193820161572e565b50919695505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a060808301526157ba60a0830184866156a3565b98975050505050505050565b600060208083850312156157d957600080fd5b825167ffffffffffffffff808211156157f157600080fd5b818501915085601f83011261580557600080fd5b81516158136153c48261537f565b81815260059190911b8301840190848101908883111561583257600080fd5b8585015b838110156158a65780518581111561584e5760008081fd5b8601603f81018b136158605760008081fd5b8781015160406158726153c48361540e565b8281528d828486010111156158875760008081fd5b615896838c8301848701614eb7565b8652505050918601918601615836565b5098975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080835481600182811c9150808316806158fe57607f831692505b60208084108203615936577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b81801561594a576001811461597d576159aa565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528415158502890196506159aa565b60008a81526020902060005b868110156159a25781548b820152908501908301615989565b505084890196505b509498975050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015615a2b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452615a19858351614edb565b945092850192908501906001016159df565b5092979650505050505050565b60006080820173ffffffffffffffffffffffffffffffffffffffff8088168452602060808186015282885180855260a087019150828a01945060005b81811015615a92578551851683529483019491830191600101615a74565b5050858103604087015287518082529082019350915080870160005b83811015615aca57815185529382019390820190600101615aae565b505050508281036060840152615ae08185614edb565b979650505050505050565b60008251615afd818460208701614eb7565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615b3f816017850160208801614eb7565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351615b7c816028840160208801614eb7565b01602801949350505050565b8082028115828204841417610fb357610fb36155b0565b600081615bae57615bae6155b0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122071f10f1d018986459eb939b84fda173cf15afea0db64873f021917aae20202f964736f6c63430008150033

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


Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.