ETH Price: $2,070.39 (+11.75%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw To Vaul...196384942024-04-12 9:00:35684 days ago1712912435IN
0x537eE18C...11c3d3575
0 ETH0.0076109917.8079224
Reinvest196384822024-04-12 8:58:11684 days ago1712912291IN
0x537eE18C...11c3d3575
0 ETH0.0169666523.57242925
Harvest196384792024-04-12 8:57:35684 days ago1712912255IN
0x537eE18C...11c3d3575
0 ETH0.0033247920.39462942
Harvest195954022024-04-06 8:10:11690 days ago1712391011IN
0x537eE18C...11c3d3575
0 ETH0.0027362414.86959974
Harvest193128412024-02-26 16:10:11730 days ago1708963811IN
0x537eE18C...11c3d3575
0 ETH0.012684967.70447895
Harvest191126642024-01-29 14:05:11758 days ago1706537111IN
0x537eE18C...11c3d3575
0 ETH0.0030256716.20714008
Harvest188215132023-12-19 17:35:11799 days ago1703007311IN
0x537eE18C...11c3d3575
0 ETH0.0117557462.52226963
Harvest187249392023-12-06 4:35:11812 days ago1701837311IN
0x537eE18C...11c3d3575
0 ETH0.0087328246.777671
Harvest185728602023-11-14 21:35:11833 days ago1699997711IN
0x537eE18C...11c3d3575
0 ETH0.0064309139.12699219
Reinvest178911352023-08-11 10:45:59929 days ago1691750759IN
0x537eE18C...11c3d3575
0 ETH0.0062632417.56556049
Reinvest178910442023-08-11 10:27:11929 days ago1691749631IN
0x537eE18C...11c3d3575
0 ETH0.0056696919.68775237
Harvest178906842023-08-11 9:14:35929 days ago1691745275IN
0x537eE18C...11c3d3575
0 ETH0.0032294917.36098995
Reinvest174806362023-06-14 20:45:23986 days ago1686775523IN
0x537eE18C...11c3d3575
0 ETH0.0130030445.04795824
Reinvest174806352023-06-14 20:45:11986 days ago1686775511IN
0x537eE18C...11c3d3575
0 ETH0.0143977843.4161318
Harvest174806222023-06-14 20:42:35986 days ago1686775355IN
0x537eE18C...11c3d3575
0 ETH0.0086519245.1514575
Reinvest172358212023-05-11 8:08:111021 days ago1683792491IN
0x537eE18C...11c3d3575
0 ETH0.0283567198.68800485
Reinvest172334142023-05-11 0:00:111021 days ago1683763211IN
0x537eE18C...11c3d3575
0 ETH0.0298629981.56859338
Harvest172317812023-05-10 18:30:111022 days ago1683743411IN
0x537eE18C...11c3d3575
0 ETH0.0183398998.94628783
Reinvest172192842023-05-09 0:18:111023 days ago1683591491IN
0x537eE18C...11c3d3575
0 ETH0.018970566.087112
Harvest171913732023-05-05 2:10:111027 days ago1683252611IN
0x537eE18C...11c3d3575
0 ETH0.02247896118.96903556
Harvest171890902023-05-04 18:30:111028 days ago1683225011IN
0x537eE18C...11c3d3575
0 ETH0.0182117197.55163205
Harvest171670002023-05-01 15:55:111031 days ago1682956511IN
0x537eE18C...11c3d3575
0 ETH0.0157623885.04027507
Reinvest171628972023-05-01 2:04:111031 days ago1682906651IN
0x537eE18C...11c3d3575
0 ETH0.0159287660.99235052
Harvest171564452023-04-30 4:20:111032 days ago1682828411IN
0x537eE18C...11c3d3575
0 ETH0.0068357736.8799791
Reinvest171495052023-04-29 4:53:471033 days ago1682744027IN
0x537eE18C...11c3d3575
0 ETH0.0128689435.59125275
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x415565b0196384822024-04-12 8:58:11684 days ago1712912291
0x537eE18C...11c3d3575
0.58967832 ETH
Transfer195954022024-04-06 8:10:11690 days ago1712391011
0x537eE18C...11c3d3575
0.34190038 ETH
Transfer193128412024-02-26 16:10:11730 days ago1708963811
0x537eE18C...11c3d3575
0.00332704 ETH
Transfer191126642024-01-29 14:05:11758 days ago1706537111
0x537eE18C...11c3d3575
0.00127862 ETH
Transfer188215132023-12-19 17:35:11799 days ago1703007311
0x537eE18C...11c3d3575
0.088437 ETH
Transfer187249392023-12-06 4:35:11812 days ago1701837311
0x537eE18C...11c3d3575
0.15473526 ETH
Sell To Uniswap178910442023-08-11 10:27:11929 days ago1691749631
0x537eE18C...11c3d3575
0.00112314 ETH
Transfer178906842023-08-11 9:14:35929 days ago1691745275
0x537eE18C...11c3d3575
0.00112314 ETH
Sell To Uniswap174806362023-06-14 20:45:23986 days ago1686775523
0x537eE18C...11c3d3575
0.07535953 ETH
Transfer174806222023-06-14 20:42:35986 days ago1686775355
0x537eE18C...11c3d3575
0.07535953 ETH
Sell To Uniswap172358212023-05-11 8:08:111021 days ago1683792491
0x537eE18C...11c3d3575
0.02547858 ETH
Transfer172317812023-05-10 18:30:111022 days ago1683743411
0x537eE18C...11c3d3575
0.02547858 ETH
Sell To Uniswap172192842023-05-09 0:18:111023 days ago1683591491
0x537eE18C...11c3d3575
0.0017807 ETH
Transfer171913732023-05-05 2:10:111027 days ago1683252611
0x537eE18C...11c3d3575
0.00049934 ETH
Transfer171890902023-05-04 18:30:111028 days ago1683225011
0x537eE18C...11c3d3575
0.00086979 ETH
Transfer171670002023-05-01 15:55:111031 days ago1682956511
0x537eE18C...11c3d3575
0.00041156 ETH
Sell To Uniswap171628972023-05-01 2:04:111031 days ago1682906651
0x537eE18C...11c3d3575
0.0022554 ETH
Transfer171564452023-04-30 4:20:111032 days ago1682828411
0x537eE18C...11c3d3575
0.0022554 ETH
Sell To Uniswap171455942023-04-28 15:44:111034 days ago1682696651
0x537eE18C...11c3d3575
0.00037188 ETH
Transfer171437932023-04-28 9:40:111034 days ago1682674811
0x537eE18C...11c3d3575
0.00037188 ETH
Sell To Uniswap171175022023-04-24 17:02:111038 days ago1682355731
0x537eE18C...11c3d3575
0.00038488 ETH
Transfer171005312023-04-22 7:55:111040 days ago1682150111
0x537eE18C...11c3d3575
0.00038488 ETH
Sell To Uniswap170971732023-04-21 20:38:111040 days ago1682109491
0x537eE18C...11c3d3575
0.00078732 ETH
Transfer170971582023-04-21 20:35:111040 days ago1682109311
0x537eE18C...11c3d3575
0.00078732 ETH
Sell To Uniswap170703502023-04-18 1:32:111044 days ago1681781531
0x537eE18C...11c3d3575
0.00758845 ETH
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LiquityStrategy

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
// SPDX-License-Identifier: MIT
pragma solidity =0.8.10;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

import {PercentMath} from "../../lib/PercentMath.sol";
import {IStrategy} from "../IStrategy.sol";
import {CustomErrors} from "../../interfaces/CustomErrors.sol";
import {IVault} from "../../vault/IVault.sol";
import {IStabilityPool} from "../../interfaces/liquity/IStabilityPool.sol";
import {ERC165Query} from "../../lib/ERC165Query.sol";
import {ICurveExchange} from "../../interfaces/curve/ICurveExchange.sol";

/***
 * Liquity Strategy generates yield by investing LUSD assets into Liquity Stability Pool contract.
 * Stability pool gives out LQTY & ETH as rewards for liquidity providers.
 * The LQTY rewards are normal yield rewards
 * But the Stability Pool achievs ETH rewards by Liquidating Troves using the LUSD we deposited.
 * So our balance of LUSD goes down and we get an 1.1x (or higher) value of ETH. In short, we make a 10% profit in ETH everytime our LUSD is used for liquidation by the stability pool
 * the harvest method here withdraws those LQTY & ETH rewards, swaps them into LUSD and then deposits them back to the stability pool.
 * we should make sure to harvest at regular intervals because if the value of ETH rewards goes below 1x of the LUSD used for liquidation then we will make a net loss on our LUSD.
 * the contract uses 0xapi for swapping the tokens.
 */
contract LiquityStrategy is
    IStrategy,
    UUPSUpgradeable,
    AccessControlUpgradeable,
    CustomErrors
{
    using PercentMath for uint256;
    using ERC165Query for address;

    error StrategyCallerNotKeeper();
    error StrategyETHSwapFailed();
    error StrategyKeeperCannotBe0Address();
    error StrategyLQTYSwapFailed();
    error StrategyNotEnoughETH();
    error StrategyNotEnoughLQTY();
    error StrategyNothingToReinvest();
    error StrategyStabilityPoolCannotBeAddressZero();
    error StrategySwapTargetCannotBe0Address();
    error StrategySwapTargetNotAllowed();
    error StrategyTokenApprovalFailed(address token);
    error StrategyTokenTransferFailed(address token);
    error StrategyInsufficientOutputAmount();
    error StrategyYieldTokenCannotBe0Address();

    event StrategyReinvested(uint256 amountInLUSD);

    address public constant WETH_CURVE_POOL =
        0xD51a44d3FaE010294C616388b506AcdA1bfAAE46;
    address public constant LUSD_CURVE_POOL =
        0xEd279fDD11cA84bEef15AF5D39BB4d4bEE23F0cA;
    address public constant CURVE_ROUTER =
        0x81C46fECa27B31F3ADC2b91eE4be9717d1cd3DD7;
    address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address public constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;

    // role allowed to invest/withdraw assets to/from the strategy (vault)
    bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
    // role allowed to call harvest() and reinvest()
    bytes32 public constant KEEPER_ROLE = keccak256("KEEPER_ROLE");
    // role for managing swap targets whitelist
    bytes32 public constant SETTINGS_ROLE = keccak256("SETTINGS_ROLE");

    IERC20 public underlying; // LUSD token
    /// @inheritdoc IStrategy
    address public override(IStrategy) vault;
    IStabilityPool public stabilityPool;
    IERC20 public lqty; // reward token
    mapping(address => bool) public allowedSwapTargets; // whitelist of swap targets

    //
    // Modifiers
    //

    modifier onlyManager() {
        if (!hasRole(MANAGER_ROLE, msg.sender))
            revert StrategyCallerNotManager();
        _;
    }

    modifier onlyKeeper() {
        if (!hasRole(KEEPER_ROLE, msg.sender)) revert StrategyCallerNotKeeper();
        _;
    }

    modifier onlySettings() {
        if (!hasRole(SETTINGS_ROLE, msg.sender))
            revert StrategyCallerNotSettings();
        _;
    }

    modifier onlyAdmin() {
        if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender))
            revert StrategyCallerNotAdmin();
        _;
    }

    //
    // Initialize method (constructor alternative for proxy contracts)
    //

    function initialize(
        address _vault,
        address _admin,
        address _stabilityPool,
        address _lqty,
        address _underlying,
        address _keeper
    ) public initializer {
        __AccessControl_init();
        __UUPSUpgradeable_init();

        if (_admin == address(0)) revert StrategyAdminCannotBe0Address();
        if (_lqty == address(0)) revert StrategyYieldTokenCannotBe0Address();
        if (_stabilityPool == address(0))
            revert StrategyStabilityPoolCannotBeAddressZero();
        if (_underlying == address(0))
            revert StrategyUnderlyingCannotBe0Address();
        if (!_vault.doesContractImplementInterface(type(IVault).interfaceId))
            revert StrategyNotIVault();
        if (_keeper == address(0)) revert StrategyKeeperCannotBe0Address();

        _grantRole(DEFAULT_ADMIN_ROLE, _admin);
        _grantRole(KEEPER_ROLE, _admin);
        _grantRole(SETTINGS_ROLE, _admin);
        _grantRole(MANAGER_ROLE, _vault);
        _grantRole(KEEPER_ROLE, _keeper);

        vault = _vault;
        underlying = IERC20(_underlying);
        stabilityPool = IStabilityPool(_stabilityPool);
        lqty = IERC20(_lqty);

        if (!underlying.approve(_stabilityPool, type(uint256).max)) {
            revert StrategyTokenApprovalFailed(_underlying);
        }
    }

    /**
     * Transfers administrator rights for the Strategy to another account,
     * revoking current admin roles and setting up the roles for the new admin.
     *
     * @notice Can only be called by the account with the ADMIN role.
     *
     * @param _newAdmin The new Strategy admin account.
     */
    function transferAdminRights(address _newAdmin) external onlyAdmin {
        if (_newAdmin == address(0x0)) revert StrategyAdminCannotBe0Address();

        if (_newAdmin == msg.sender)
            revert StrategyCannotTransferAdminRightsToSelf();

        _grantRole(DEFAULT_ADMIN_ROLE, _newAdmin);
        _grantRole(KEEPER_ROLE, _newAdmin);
        _grantRole(SETTINGS_ROLE, _newAdmin);

        _revokeRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _revokeRole(KEEPER_ROLE, msg.sender);
        _revokeRole(SETTINGS_ROLE, msg.sender);
    }

    //
    // IStrategy
    //

    /**
     * Returns true since strategy is synchronous.
     */
    function isSync() external pure override(IStrategy) returns (bool) {
        return true;
    }

    /// @inheritdoc IStrategy
    function hasAssets()
        external
        view
        virtual
        override(IStrategy)
        returns (bool)
    {
        return investedAssets() != 0;
    }

    /// @inheritdoc IStrategy
    /// @notice LQTY rewards of the strategy waiting to be claimed in the liquity stability pool are not included
    /// @notice but the ETH rewards are included
    function investedAssets()
        public
        view
        virtual
        override(IStrategy)
        returns (uint256)
    {
        uint256 ethBalance = address(this).balance +
            stabilityPool.getDepositorETHGain(address(this));

        // need to do this because the get_exchange_amount method reverts if ethBalance is zero
        if (ethBalance == 0) {
            return stabilityPool.getCompoundedLUSDDeposit(address(this));
        }

        uint256 ethBalanceInUSDT = ICurveExchange(CURVE_ROUTER)
            .get_exchange_amount(WETH_CURVE_POOL, WETH, USDT, ethBalance);

        uint256 ethBalanceInLusd = ICurveExchange(CURVE_ROUTER)
            .get_exchange_amount(
                LUSD_CURVE_POOL,
                USDT,
                address(underlying),
                ethBalanceInUSDT
            );

        return
            stabilityPool.getCompoundedLUSDDeposit(address(this)) +
            ethBalanceInLusd;
    }

    /// @inheritdoc IStrategy
    /// @notice this will also claim any unclaimed gains in the stability pool
    function invest() external virtual override(IStrategy) onlyManager {
        uint256 balance = underlying.balanceOf(address(this));
        if (balance == 0) revert StrategyNoUnderlying();

        // claims LQTY & ETH rewards if there are any
        stabilityPool.provideToSP(balance, address(0));

        emit StrategyInvested(balance);
    }

    /// @inheritdoc IStrategy
    /// @notice will also claim unclaimed LQTY & ETH gains
    /// @notice when amount > total deposited, all available funds will be withdrawn
    function withdrawToVault(uint256 amount)
        external
        virtual
        override(IStrategy)
        onlyManager
    {
        if (amount == 0) revert StrategyAmountZero();
        if (amount > investedAssets()) revert StrategyNotEnoughShares();

        // withdraws underlying amount and claims LQTY & ETH rewards
        stabilityPool.withdrawFromSP(amount);

        // use balance instead of amount since amount could be greater than what was actually withdrawn
        uint256 balance = underlying.balanceOf(address(this));
        if (!underlying.transfer(vault, balance)) {
            revert StrategyTokenTransferFailed(address(underlying));
        }

        emit StrategyWithdrawn(balance);
    }

    /**
     * Allows an address to be used as a swap target by adding it on the whitelist.
     *
     * @notice Can only be called by the account with the SETTINGS role.
     * @notice Swap targets are addresses of 0x contracts used for swapping ETH and LQTY tokens held by the strategy.
     */
    function allowSwapTarget(address _swapTarget) external onlySettings {
        _checkSwapTargetForZeroAddress(_swapTarget);

        allowedSwapTargets[_swapTarget] = true;
    }

    /**
     * Denies an address to be used as a swap target by removing it from the whitelist.
     *
     * @notice Can only be called by the account with the SETTINGS role.
     */
    function denySwapTarget(address _swapTarget) external onlySettings {
        _checkSwapTargetForZeroAddress(_swapTarget);

        allowedSwapTargets[_swapTarget] = false;
    }

    /**
     * Collects the LQTY & ETH rewards from the stability pool.
     */
    function harvest() external virtual {
        // call to withdrawFromSP with 0 amount will only claim rewards
        stabilityPool.withdrawFromSP(0);
    }

    /**
     * Swaps LQTY tokens and ETH held by the strategy to LUSD,
     * and reinvests the swapped LUSD amount into the stability pool.
     *
     * @notice Can only be called by the account with the KEEPER role.
     * @notice Swap data arguments provided are real-time data obtained from '0x' api.
     *
     * @param _swapTarget the address of the '0x' contract performing the tokens swap.
     * @param _lqtySwapData data used to perform LQTY -> LUSD swap. Leave empty to skip this swap.
     * @param _ethAmount amount of ETH to swap to LUSD, has to match with the amount used to obtain @param _ethSwapData.
     * @param _ethSwapData data used to perform ETH -> LUSD swap. Leave empty to skip this swap.
     * @param _amountOutMin the minimum amount of LUSD to be received after the ETH & LQTY -> LUSD swap.
     */
    function reinvest(
        address _swapTarget,
        uint256 _lqtyAmount,
        bytes calldata _lqtySwapData,
        uint256 _ethAmount,
        bytes calldata _ethSwapData,
        uint256 _amountOutMin
    ) external virtual onlyKeeper {
        _checkSwapTargetForZeroAddress(_swapTarget);

        if (!allowedSwapTargets[_swapTarget])
            revert StrategySwapTargetNotAllowed();

        swapLQTYtoLUSD(_lqtyAmount, _swapTarget, _lqtySwapData);
        swapETHtoLUSD(_ethAmount, _swapTarget, _ethSwapData);

        // reinvest LUSD gains into the stability pool
        uint256 balance = underlying.balanceOf(address(this));
        if (balance == 0) {
            revert StrategyNothingToReinvest();
        }

        if (balance < _amountOutMin) {
            revert StrategyInsufficientOutputAmount();
        }

        emit StrategyReinvested(balance);

        stabilityPool.provideToSP(balance, address(0));
    }

    /**
     * Checks if the provided swap target is 0 address and reverts if true.
     */
    function _checkSwapTargetForZeroAddress(address _swapTarget) internal pure {
        if (_swapTarget == address(0))
            revert StrategySwapTargetCannotBe0Address();
    }

    /**
     * Swaps LQTY tokens held by the strategy to LUSD.
     *
     * @notice Swap data is real-time data obtained from '0x' api.
     *
     * @param _amount the amount of LQTY tokens to swap. Has to match with the amount used to obtain @param _lqtySwapData from '0x' api.
     * @param _swapTarget the address of the '0x' contract performing the swap.
     * @param _lqtySwapData data from '0x' api used to perform LQTY -> LUSD swap.
     */
    function swapLQTYtoLUSD(
        uint256 _amount,
        address _swapTarget,
        bytes calldata _lqtySwapData
    ) internal {
        // don't do cross-contract call if nothing to swap
        if (_amount == 0 || _lqtySwapData.length == 0) return;

        uint256 lqtyBalance = lqty.balanceOf(address(this));
        if (_amount > lqtyBalance) revert StrategyNotEnoughLQTY();

        // give approval to the swapTarget
        if (!lqty.approve(_swapTarget, _amount)) {
            revert StrategyTokenApprovalFailed(address(lqty));
        }

        // perform the swap
        (bool success, ) = _swapTarget.call{value: 0}(_lqtySwapData);
        if (!success) revert StrategyLQTYSwapFailed();
    }

    /**
     * Swaps ETH held by the strategy to LUSD.
     *
     * @notice Swap data is real-time data obtained from '0x' api.
     *
     * @param _amount the amount of ETH to swap. Has to match with the amount used to obtain @param _ethSwapData from '0x' api.
     * @param _swapTarget the address of the '0x' contract performing the swap.
     * @param _ethSwapData data from '0x' api to perform ETH -> LUSD swap.
     */
    function swapETHtoLUSD(
        uint256 _amount,
        address _swapTarget,
        bytes calldata _ethSwapData
    ) internal {
        // don't do cross-contract call if nothing to swap
        if (_amount == 0 || _ethSwapData.length == 0) return;

        uint256 ethBalance = address(this).balance;
        if (_amount > ethBalance) revert StrategyNotEnoughETH();

        (bool success, ) = _swapTarget.call{value: _amount}(_ethSwapData);
        if (!success) revert StrategyETHSwapFailed();
    }

    function _authorizeUpgrade(address newImplementation)
        internal
        override
        onlyAdmin
    {}

    /**
     * Strategy has to be able to receive ETH as stability pool rewards.
     */
    receive() external payable {}
}

// 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.7.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.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 AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    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(IAccessControlUpgradeable).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 ",
                        StringsUpgradeable.toHexString(uint160(account), 20),
                        " is missing role ",
                        StringsUpgradeable.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());
        }
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.0;

import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 *
 * _Available since v4.1._
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
    address private immutable __self = address(this);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        require(address(this) != __self, "Function must be called through delegatecall");
        require(_getImplementation() == __self, "Function must be called through active proxy");
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
        _;
    }

    /**
     * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate that the this implementation remains valid after an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
        return _IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeTo(address newImplementation) external virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data, true);
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeTo} and {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal override onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

library PercentMath {
    // Divisor used for representing percentages
    uint256 public constant PCT_DIVISOR = 10000;

    /**
     * @dev Returns whether an amount is a valid percentage out of PCT_DIVISOR
     * @param _amount Amount that is supposed to be a percentage
     */
    function validPct(uint256 _amount) internal pure returns (bool) {
        return _amount <= PCT_DIVISOR;
    }

    /**
     * @dev Compute percentage of a value with the percentage represented by a fraction over PERC_DIVISOR
     * @param _amount Amount to take the percentage of
     * @param _fracNum Numerator of fraction representing the percentage with PCT_DIVISOR as the denominator
     */
    function pctOf(uint256 _amount, uint16 _fracNum)
        internal
        pure
        returns (uint256)
    {
        return (_amount * _fracNum) / PCT_DIVISOR;
    }

    /**
     * @dev Checks if a given number corresponds to 100%
     * @param _perc Percentage value to check, with PCT_DIVISOR
     */
    function is100Pct(uint256 _perc) internal pure returns (bool) {
        return _perc == PCT_DIVISOR;
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

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

/**
 * IStrategy defines the interface for pluggable contracts used by vaults to invest funds and generate yield.
 *
 * @notice It's up to the strategy to decide what do to with investable assets provided by a vault.
 *
 * @notice It's up to the vault to decide how much to invest/disinvest from the total pool.
 */
interface IStrategy {
    /**
     * Emmited when funds are invested by the strategy.
     *
     *@param amount amount invested
     */
    event StrategyInvested(uint256 amount);
    /**
     * Emmited when funds are withdrawn (disinvested) by the strategy.
     *
     *@param amount amount withdrawn
     */
    event StrategyWithdrawn(uint256 amount);

    /**
     * Provides information about wether the strategy is synchronous or asynchronous.
     *
     * @notice Synchronous strategies support instant withdrawals,
     * while asynchronous strategies impose a delay before withdrawals can be made.
     *
     * @return true if the strategy is synchronous, false otherwise
     */
    function isSync() external view returns (bool);

    /**
     * The vault linked to this strategy.
     *
     * @return The vault's address
     */
    function vault() external view returns (address);

    /**
     * Withdraws the specified amount back to the vault (disinvests)
     *
     * @param amount Amount to withdraw
     */
    function withdrawToVault(uint256 amount) external;

    /**
     * Amount of the underlying currency currently invested by the strategy.
     *
     * @notice both held and invested amounts are included here, using the
     * latest known exchange rates to the underlying currency
     *
     * @return The total amount of underlying
     */
    function investedAssets() external view returns (uint256);

    /**
     * Indicates if assets are invested into strategy or not.
     *
     * @notice this will be used when removing the strategy from the vault
     * @return true if assets invested, false if nothing invested.
     */
    function hasAssets() external view returns (bool);

    /**
     * Deposits of all the available underlying into the yield generating protocol.
     */
    function invest() external;
}

File 7 of 23 : CustomErrors.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

interface CustomErrors {
    //
    // Vault Errors
    //

    // Vault: sender is not the owner of the group id
    error VaultSenderNotOwnerOfGroupId();

    // Vault: invalid investPct
    error VaultInvalidInvestpct();

    // Vault: invalid performance fee
    error VaultInvalidPerformanceFee();

    // Vault: no performance fee
    error VaultNoPerformanceFee();

    // Vault: invalid lossTolerance
    error VaultInvalidLossTolerance();

    // Vault: underlying cannot be 0x0
    error VaultUnderlyingCannotBe0Address();

    // Vault: treasury cannot be 0x0
    error VaultTreasuryCannotBe0Address();

    // Vault: admin cannot be 0x0
    error VaultAdminCannotBe0Address();

    // Vault: cannot transfer admin rights to self
    error VaultCannotTransferAdminRightsToSelf();

    // Vault: caller is not admin
    error VaultCallerNotAdmin();

    // Vault: caller is not settings
    error VaultCallerNotSettings();

    // Vault: caller is not keeper
    error VaultCallerNotKeeper();

    // Vault: caller is not sponsor
    error VaultCallerNotSponsor();

    // Vault: destination address is 0x
    error VaultDestinationCannotBe0Address();

    // Vault: strategy is not set
    error VaultStrategyNotSet();

    // Vault: invalid minLockPeriod
    error VaultInvalidMinLockPeriod();

    // Vault: invalid lock period
    error VaultInvalidLockPeriod();

    // Vault: cannot deposit 0
    error VaultCannotDeposit0();

    // Vault: cannot sponsor 0
    error VaultCannotSponsor0();

    // Vault: cannot deposit when yield is negative
    error VaultCannotDepositWhenYieldNegative();

    // Vault: cannot deposit when the claimer is in debt
    error VaultCannotDepositWhenClaimerInDebt();

    // Vault: cannot withdraw when yield is negative
    error VaultCannotWithdrawWhenYieldNegative();

    // Vault: nothing to do
    error VaultNothingToDo();

    // Vault: not enough to rebalance
    error VaultNotEnoughToRebalance();

    // Vault: invalid vault
    error VaultInvalidVault();

    // Vault: strategy has invested funds
    error VaultStrategyHasInvestedFunds();

    // Vault: not enough funds
    error VaultNotEnoughFunds();

    // Vault: you are not allowed
    error VaultNotAllowed();

    // Vault: amount is locked
    error VaultAmountLocked();

    // Vault: deposit is locked
    error VaultDepositLocked();

    // Vault: token id is not a sponsor
    error VaultNotSponsor();

    // Vault: token id is not a deposit
    error VaultNotDeposit();

    // Vault: claim percentage cannot be 0
    error VaultClaimPercentageCannotBe0();

    // Vault: claimer cannot be address 0
    error VaultClaimerCannotBe0();

    // Vault: claims don't add up to 100%
    error VaultClaimsDontAddUp();

    // Vault: you are not the owner of a deposit
    error VaultNotOwnerOfDeposit();

    // Vault: cannot withdraw more than the available amount
    error VaultCannotWithdrawMoreThanAvailable();

    // Vault: must force withdraw to withdraw with a loss
    error VaultMustUseForceWithdrawToAcceptLosses();

    // Vault: amount received does not match params
    error VaultAmountDoesNotMatchParams();

    // Vault: cannot compute shares when there's no principal
    error VaultCannotComputeSharesWithoutPrincipal();

    // Vault: deposit name for MetaVault too short
    error VaultDepositNameTooShort();

    // Vault: no yield to claim
    error VaultNoYieldToClaim();

    //
    // Strategy Errors
    //

    // Strategy: admin is 0x
    error StrategyAdminCannotBe0Address();

    // Strategy: cannot transfer admin rights to self
    error StrategyCannotTransferAdminRightsToSelf();

    // Strategy: underlying is 0x
    error StrategyUnderlyingCannotBe0Address();

    // Strategy: not an IVault
    error StrategyNotIVault();

    // Strategy: caller is not manager
    error StrategyCallerNotManager();

    // Strategy: caller has no settings role
    error StrategyCallerNotSettings();

    // Strategy: caller is not admin
    error StrategyCallerNotAdmin();

    // Strategy: amount is 0
    error StrategyAmountZero();

    // Strategy: not running
    error StrategyNotRunning();

    // Not Enough Underlying Balance in Strategy contract
    error StrategyNoUnderlying();

    // Not Enough Shares in Strategy Contract
    error StrategyNotEnoughShares();
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IVault {
    //
    // Structs
    //

    struct ClaimParams {
        uint16 pct;
        address beneficiary;
        bytes data;
    }

    struct DepositParams {
        address inputToken;
        uint64 lockDuration;
        uint256 amount;
        ClaimParams[] claims;
        string name;
        uint256 slippage;
    }

    struct Deposit {
        /// amount of the deposit
        uint256 amount;
        /// wallet of the owner
        address owner;
        /// wallet of the claimer
        address claimerId;
        /// when can the deposit be withdrawn
        uint256 lockedUntil;
    }

    struct Claimer {
        uint256 totalPrincipal;
        uint256 totalShares;
    }

    //
    // Events
    //

    event DepositMinted(
        uint256 indexed id,
        uint256 groupId,
        uint256 amount,
        uint256 shares,
        address indexed depositor,
        address indexed claimer,
        address claimerId,
        uint64 lockedUntil,
        bytes data,
        string name
    );

    event DepositWithdrawn(
        uint256 indexed id,
        uint256 shares,
        uint256 amount,
        address indexed to,
        bool burned
    );

    event Invested(uint256 amount);

    event Disinvested(uint256 amount);

    event YieldClaimed(
        address claimerId,
        address indexed to,
        uint256 amount,
        uint256 burnedShares,
        uint256 perfFee,
        uint256 totalUnderlying,
        uint256 totalShares
    );

    event FeeWithdrawn(uint256 amount);

    //
    // Public API
    //

    /**
     * Update the invested amount;
     */
    function updateInvested() external;

    /**
     * Calculate maximum investable amount and already invested amount
     *
     * @return maxInvestableAmount maximum investable amount
     * @return alreadyInvested already invested amount
     */
    function investState()
        external
        view
        returns (uint256 maxInvestableAmount, uint256 alreadyInvested);

    /**
     * Percentage of the total underlying to invest in the strategy
     */
    function investPct() external view returns (uint16);

    /**
     * Underlying ERC20 token accepted by the vault
     */
    function underlying() external view returns (IERC20Metadata);

    /**
     * Minimum lock period for each deposit
     */
    function minLockPeriod() external view returns (uint64);

    /**
     * Total amount of underlying currently controlled by the
     * vault and the its strategy.
     */
    function totalUnderlying() external view returns (uint256);

    /**
     * Total amount of shares
     */
    function totalShares() external view returns (uint256);

    /**
     * Computes the amount of yield available for an an address.
     *
     * @param _to address to consider.
     *
     * @return claimable yield for @param _to, share of generated yield by @param _to,
     *      and performance fee from generated yield
     */
    function yieldFor(address _to)
        external
        view
        returns (
            uint256,
            uint256,
            uint256
        );

    /**
     * Accumulate performance fee and transfers rest yield generated for the caller to
     *
     * @param _to Address that will receive the yield.
     */
    function claimYield(address _to) external;

    /**
     * Creates a new deposit using the specified group id
     *
     * @param _groupId The group id for the new deposit
     * @param _params Deposit params
     */
    function depositForGroupId(uint256 _groupId, DepositParams calldata _params)
        external
        returns (uint256[] memory);

    /**
     * Creates a new deposit
     *
     * @param _params Deposit params
     */
    function deposit(DepositParams calldata _params)
        external
        returns (uint256[] memory);

    /**
     * Withdraws the principal from the deposits with the ids provided in @param _ids and sends it to @param _to.
     *
     * It fails if the vault is underperforming and there are not enough funds
     * to withdraw the expected amount.
     *
     * @param _to Address that will receive the funds.
     * @param _ids Array with the ids of the deposits.
     */
    function withdraw(address _to, uint256[] calldata _ids) external;

    /**
     * Withdraws the principal from the deposits with the ids provided in @param _ids and sends it to @param _to.
     *
     * When the vault is underperforming it withdraws the funds with a loss.
     *
     * @param _to Address that will receive the funds.
     * @param _ids Array with the ids of the deposits.
     */
    function forceWithdraw(address _to, uint256[] calldata _ids) external;

    /**
     * Withdraws any pending performance fee amount back to the treasury
     */
    function withdrawPerformanceFee() external;
}

// SPDX-License-Identifier: MIT
pragma solidity =0.8.10;

interface IStabilityPool {
    /*  provideToSP():
     *
     * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends
     * - Tags the deposit with the provided front end tag param, if it's a new deposit
     * - Sends depositor's accumulated gains (LQTY, ETH) to depositor
     * - Sends the tagged front end's accumulated LQTY gains to the tagged front end
     * - Increases deposit and tagged front end's stake, and takes new snapshots for each.
     */
    function provideToSP(uint256 _amount, address _frontEndTag) external;

    /*  withdrawFromSP():
     *
     * - Triggers a LQTY issuance, based on time passed since the last issuance. The LQTY issuance is shared between *all* depositors and front ends
     * - Removes the deposit's front end tag if it is a full withdrawal
     * - Sends all depositor's accumulated gains (LQTY, ETH) to depositor
     * - Sends the tagged front end's accumulated LQTY gains to the tagged front end
     * - Decreases deposit and tagged front end's stake, and takes new snapshots for each.
     *
     * If _amount > userDeposit, the user withdraws all of their compounded deposit.
     */
    function withdrawFromSP(uint256 _amount) external;

    /* Calculates the ETH gain earned by the deposit since its last snapshots were taken.
     * Given by the formula:  E = d0 * (S - S(0))/P(0)
     * where S(0) and P(0) are the depositor's snapshots of the sum S and product P, respectively.
     * d0 is the last recorded deposit value.
     */
    function getDepositorETHGain(address _depositor)
        external
        view
        returns (uint256);

    /*
     * Calculate the LQTY gain earned by a deposit since its last snapshots were taken.
     * Given by the formula:  LQTY = d0 * (G - G(0))/P(0)
     * where G(0) and P(0) are the depositor's snapshots of the sum G and product P, respectively.
     * d0 is the last recorded deposit value.
     */
    function getDepositorLQTYGain(address _depositor)
        external
        view
        returns (uint256);

    /*
     * Return the user's compounded deposit. Given by the formula:  d = d0 * P/P(0)
     * where P(0) is the depositor's snapshot of the product P, taken when they last updated their deposit.
     */
    function getCompoundedLUSDDeposit(address _depositor)
        external
        view
        returns (uint256);

    /*
     * Cancels out the specified debt against the LUSD contained in the Stability Pool (as far as possible)
     * and transfers the Trove's ETH collateral from ActivePool to StabilityPool.
     * Only called by liquidation functions in the TroveManager.
     */
    function offset(uint256 _debtToOffset, uint256 _collToAdd) external;

    /*
     * Address of the TroveManager contract.
     */
    function troveManager() external view returns (address);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

library ERC165Query {
    bytes4 constant InvalidID = 0xffffffff;
    bytes4 constant ERC165ID = 0x01ffc9a7;

    function doesContractImplementInterface(
        address _contract,
        bytes4 _interfaceId
    ) internal view returns (bool) {
        uint256 success;
        uint256 result;

        (success, result) = noThrowCall(_contract, ERC165ID);
        if ((success == 0) || (result == 0)) {
            return false;
        }

        (success, result) = noThrowCall(_contract, InvalidID);
        if ((success == 0) || (result != 0)) {
            return false;
        }

        (success, result) = noThrowCall(_contract, _interfaceId);
        if ((success == 1) && (result == 1)) {
            return true;
        }
        return false;
    }

    function noThrowCall(address _contract, bytes4 _interfaceId)
        internal
        view
        returns (uint256 success, uint256 result)
    {
        bytes4 erc165ID = ERC165ID;

        assembly {
            let x := mload(0x40) // Find empty storage location using "free memory pointer"
            mstore(x, erc165ID) // Place signature at beginning of empty storage
            mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature

            success := staticcall(
                30000, // 30k gas
                _contract, // To addr
                x, // Inputs are stored at location x
                0x24, // Inputs are 36 bytes long
                x, // Store output over input (saves space)
                0x20
            ) // Outputs are 32 bytes long

            result := mload(x) // Load the result
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity =0.8.10;

interface ICurveExchange {
    function get_exchange_amount(
        address _pool,
        address _from,
        address _to,
        uint256 _amount
    ) external view returns (uint256);
}

// 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 IAccessControlUpgradeable {
    /**
     * @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;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _HEX_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) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

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

    /**
     * @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 "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.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 ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 16 of 23 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
     * initialization step. This is essential to configure modules that are added through upgrades and that require
     * initialization.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

// 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 IERC165Upgradeable {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 19 of 23 : draft-IERC1822Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822ProxiableUpgradeable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeaconUpgradeable.sol";
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/StorageSlotUpgradeable.sol";
import "../utils/Initializable.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 *
 * @custom:oz-upgrades-unsafe-allow delegatecall
 */
abstract contract ERC1967UpgradeUpgradeable is Initializable {
    function __ERC1967Upgrade_init() internal onlyInitializing {
    }

    function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
    }
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            _functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallUUPS(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        // Upgrades from old implementations will perform a rollback test. This test requires the new
        // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
        // this special case will break upgrade paths from old UUPS implementation to new ones.
        if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
            _setImplementation(newImplementation);
        } else {
            try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
            } catch {
                revert("ERC1967Upgrade: new implementation is not UUPS");
            }
            _upgradeToAndCall(newImplementation, data, forceCall);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Emitted when the beacon is upgraded.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
        }
    }

    /**
     * @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) private returns (bytes memory) {
        require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 21 of 23 : IBeaconUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeaconUpgradeable {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
 */
library StorageSlotUpgradeable {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"name":"StrategyAdminCannotBe0Address","type":"error"},{"inputs":[],"name":"StrategyAmountZero","type":"error"},{"inputs":[],"name":"StrategyCallerNotAdmin","type":"error"},{"inputs":[],"name":"StrategyCallerNotKeeper","type":"error"},{"inputs":[],"name":"StrategyCallerNotManager","type":"error"},{"inputs":[],"name":"StrategyCallerNotSettings","type":"error"},{"inputs":[],"name":"StrategyCannotTransferAdminRightsToSelf","type":"error"},{"inputs":[],"name":"StrategyETHSwapFailed","type":"error"},{"inputs":[],"name":"StrategyInsufficientOutputAmount","type":"error"},{"inputs":[],"name":"StrategyKeeperCannotBe0Address","type":"error"},{"inputs":[],"name":"StrategyLQTYSwapFailed","type":"error"},{"inputs":[],"name":"StrategyNoUnderlying","type":"error"},{"inputs":[],"name":"StrategyNotEnoughETH","type":"error"},{"inputs":[],"name":"StrategyNotEnoughLQTY","type":"error"},{"inputs":[],"name":"StrategyNotEnoughShares","type":"error"},{"inputs":[],"name":"StrategyNotIVault","type":"error"},{"inputs":[],"name":"StrategyNotRunning","type":"error"},{"inputs":[],"name":"StrategyNothingToReinvest","type":"error"},{"inputs":[],"name":"StrategyStabilityPoolCannotBeAddressZero","type":"error"},{"inputs":[],"name":"StrategySwapTargetCannotBe0Address","type":"error"},{"inputs":[],"name":"StrategySwapTargetNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"StrategyTokenApprovalFailed","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"StrategyTokenTransferFailed","type":"error"},{"inputs":[],"name":"StrategyUnderlyingCannotBe0Address","type":"error"},{"inputs":[],"name":"StrategyYieldTokenCannotBe0Address","type":"error"},{"inputs":[],"name":"VaultAdminCannotBe0Address","type":"error"},{"inputs":[],"name":"VaultAmountDoesNotMatchParams","type":"error"},{"inputs":[],"name":"VaultAmountLocked","type":"error"},{"inputs":[],"name":"VaultCallerNotAdmin","type":"error"},{"inputs":[],"name":"VaultCallerNotKeeper","type":"error"},{"inputs":[],"name":"VaultCallerNotSettings","type":"error"},{"inputs":[],"name":"VaultCallerNotSponsor","type":"error"},{"inputs":[],"name":"VaultCannotComputeSharesWithoutPrincipal","type":"error"},{"inputs":[],"name":"VaultCannotDeposit0","type":"error"},{"inputs":[],"name":"VaultCannotDepositWhenClaimerInDebt","type":"error"},{"inputs":[],"name":"VaultCannotDepositWhenYieldNegative","type":"error"},{"inputs":[],"name":"VaultCannotSponsor0","type":"error"},{"inputs":[],"name":"VaultCannotTransferAdminRightsToSelf","type":"error"},{"inputs":[],"name":"VaultCannotWithdrawMoreThanAvailable","type":"error"},{"inputs":[],"name":"VaultCannotWithdrawWhenYieldNegative","type":"error"},{"inputs":[],"name":"VaultClaimPercentageCannotBe0","type":"error"},{"inputs":[],"name":"VaultClaimerCannotBe0","type":"error"},{"inputs":[],"name":"VaultClaimsDontAddUp","type":"error"},{"inputs":[],"name":"VaultDepositLocked","type":"error"},{"inputs":[],"name":"VaultDepositNameTooShort","type":"error"},{"inputs":[],"name":"VaultDestinationCannotBe0Address","type":"error"},{"inputs":[],"name":"VaultInvalidInvestpct","type":"error"},{"inputs":[],"name":"VaultInvalidLockPeriod","type":"error"},{"inputs":[],"name":"VaultInvalidLossTolerance","type":"error"},{"inputs":[],"name":"VaultInvalidMinLockPeriod","type":"error"},{"inputs":[],"name":"VaultInvalidPerformanceFee","type":"error"},{"inputs":[],"name":"VaultInvalidVault","type":"error"},{"inputs":[],"name":"VaultMustUseForceWithdrawToAcceptLosses","type":"error"},{"inputs":[],"name":"VaultNoPerformanceFee","type":"error"},{"inputs":[],"name":"VaultNoYieldToClaim","type":"error"},{"inputs":[],"name":"VaultNotAllowed","type":"error"},{"inputs":[],"name":"VaultNotDeposit","type":"error"},{"inputs":[],"name":"VaultNotEnoughFunds","type":"error"},{"inputs":[],"name":"VaultNotEnoughToRebalance","type":"error"},{"inputs":[],"name":"VaultNotOwnerOfDeposit","type":"error"},{"inputs":[],"name":"VaultNotSponsor","type":"error"},{"inputs":[],"name":"VaultNothingToDo","type":"error"},{"inputs":[],"name":"VaultSenderNotOwnerOfGroupId","type":"error"},{"inputs":[],"name":"VaultStrategyHasInvestedFunds","type":"error"},{"inputs":[],"name":"VaultStrategyNotSet","type":"error"},{"inputs":[],"name":"VaultTreasuryCannotBe0Address","type":"error"},{"inputs":[],"name":"VaultUnderlyingCannotBe0Address","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","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":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"StrategyInvested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amountInLUSD","type":"uint256"}],"name":"StrategyReinvested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"StrategyWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"CURVE_ROUTER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","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":[],"name":"LUSD_CURVE_POOL","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SETTINGS_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH_CURVE_POOL","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_swapTarget","type":"address"}],"name":"allowSwapTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowedSwapTargets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_swapTarget","type":"address"}],"name":"denySwapTarget","outputs":[],"stateMutability":"nonpayable","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":[],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"hasAssets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_stabilityPool","type":"address"},{"internalType":"address","name":"_lqty","type":"address"},{"internalType":"address","name":"_underlying","type":"address"},{"internalType":"address","name":"_keeper","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"invest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"investedAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isSync","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"lqty","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_swapTarget","type":"address"},{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"},{"internalType":"bytes","name":"_lqtySwapData","type":"bytes"},{"internalType":"uint256","name":"_ethAmount","type":"uint256"},{"internalType":"bytes","name":"_ethSwapData","type":"bytes"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"}],"name":"reinvest","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":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stabilityPool","outputs":[{"internalType":"contract IStabilityPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newAdmin","type":"address"}],"name":"transferAdminRights","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawToVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a06040523060805234801561001457600080fd5b50608051612e2a61004c600039600081816109010152818161098601528181610df101528181610e760152610f5c0152612e2a6000f3fe60806040526004361061021d5760003560e01c80636ff902e71161011d578063c54e44eb116100b0578063e49b30da1161007f578063ec87621c11610064578063ec87621c1461066e578063f6289887146106a2578063fbfa77cf146106d657600080fd5b8063e49b30da14610631578063e8b5e51f1461065957600080fd5b8063c54e44eb146105a9578063cc2a9a5b146105d1578063ce8c42e8146105f1578063d547741f1461061157600080fd5b80639af2e635116100ec5780639af2e63514610538578063a217fddf1461054c578063ad5c464814610561578063b5106add1461058957600080fd5b80636ff902e71461048a5780637f6ec455146104b2578063865a3e47146104d257806391d14854146104f257600080fd5b8063364bc15a116101b05780634ad0b6841161017f57806352d1902d1161016457806352d1902d146104405780635be9b2d3146104555780636f307dc31461046a57600080fd5b80634ad0b684146104185780634f1ef2861461042d57600080fd5b8063364bc15a1461038f57806336568abe146103c35780633659cfe6146103e35780634641257d1461040357600080fd5b806321bc5df4116101ec57806321bc5df4146102e9578063248a9ca3146103095780632f2ff15d1461034757806330d8a9e81461036757600080fd5b806301ffc9a714610229578063048c661d1461025e578063129d91ab146102965780631fa1fe36146102b857600080fd5b3661022457005b600080fd5b34801561023557600080fd5b50610249610244366004612936565b6106f6565b60405190151581526020015b60405180910390f35b34801561026a57600080fd5b5060fd5461027e906001600160a01b031681565b6040516001600160a01b039091168152602001610255565b3480156102a257600080fd5b506102b66102b136600461297c565b610746565b005b3480156102c457600080fd5b506102496102d336600461297c565b60ff602081905260009182526040909120541681565b3480156102f557600080fd5b506102b661030436600461297c565b6107bf565b34801561031557600080fd5b50610339610324366004612997565b600090815260c9602052604090206001015490565b604051908152602001610255565b34801561035357600080fd5b506102b66103623660046129b0565b61083b565b34801561037357600080fd5b5061027e73ed279fdd11ca84beef15af5d39bb4d4bee23f0ca81565b34801561039b57600080fd5b506103397ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b3480156103cf57600080fd5b506102b66103de3660046129b0565b610865565b3480156103ef57600080fd5b506102b66103fe36600461297c565b6108f6565b34801561040f57600080fd5b506102b6610a72565b34801561042457600080fd5b50610339610ad2565b6102b661043b3660046129f2565b610de6565b34801561044c57600080fd5b50610339610f4f565b34801561046157600080fd5b50610249611014565b34801561047657600080fd5b5060fb5461027e906001600160a01b031681565b34801561049657600080fd5b5061027e73d51a44d3fae010294c616388b506acda1bfaae4681565b3480156104be57600080fd5b5060fe5461027e906001600160a01b031681565b3480156104de57600080fd5b506102b66104ed366004612afd565b611025565b3480156104fe57600080fd5b5061024961050d3660046129b0565b600091825260c9602090815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561054457600080fd5b506001610249565b34801561055857600080fd5b50610339600081565b34801561056d57600080fd5b5061027e73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b34801561059557600080fd5b506102b66105a436600461297c565b611284565b3480156105b557600080fd5b5061027e73dac17f958d2ee523a2206206994597c13d831ec781565b3480156105dd57600080fd5b506102b66105ec366004612b98565b6113fb565b3480156105fd57600080fd5b506102b661060c366004612997565b61185a565b34801561061d57600080fd5b506102b661062c3660046129b0565b611afe565b34801561063d57600080fd5b5061027e7381c46feca27b31f3adc2b91ee4be9717d1cd3dd781565b34801561066557600080fd5b506102b6611b23565b34801561067a57600080fd5b506103397f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0881565b3480156106ae57600080fd5b506103397ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309581565b3480156106e257600080fd5b5060fc5461027e906001600160a01b031681565b60006001600160e01b031982167f7965db0b00000000000000000000000000000000000000000000000000000000148061074057506301ffc9a760e01b6001600160e01b03198316145b92915050565b3360009081527fdf9e05033a6c4e281e51ff7ac61d3a2fec43ac6ee7b61abf4c8074f4927147b7602052604090205460ff16610795576040516370c4ee8d60e01b815260040160405180910390fd5b61079e81611cb9565b6001600160a01b0316600090815260ff60205260409020805460ff19169055565b3360009081527fdf9e05033a6c4e281e51ff7ac61d3a2fec43ac6ee7b61abf4c8074f4927147b7602052604090205460ff1661080e576040516370c4ee8d60e01b815260040160405180910390fd5b61081781611cb9565b6001600160a01b0316600090815260ff60205260409020805460ff19166001179055565b600082815260c9602052604090206001015461085681611cf9565b6108608383611d03565b505050565b6001600160a01b03811633146108e85760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6108f28282611da5565b5050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614156109845760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b19195b1959d85d1958d85b1b60a21b60648201526084016108df565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166109df7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b031614610a4a5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b6163746976652070726f787960a01b60648201526084016108df565b610a5381611e28565b60408051600080825260208201909252610a6f91839190611e77565b50565b60fd54604051632e54bf9560e01b8152600060048201526001600160a01b0390911690632e54bf9590602401600060405180830381600087803b158015610ab857600080fd5b505af1158015610acc573d6000803e3d6000fd5b50505050565b60fd546040517f389e92a500000000000000000000000000000000000000000000000000000000815230600482015260009182916001600160a01b039091169063389e92a590602401602060405180830381865afa158015610b38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b5c9190612c0c565b610b669047612c3b565b905080610bde5760fd54604051621cdc4760e81b81523060048201526001600160a01b0390911690631cdc470090602401602060405180830381865afa158015610bb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd89190612c0c565b91505090565b604051630e5cfa0d60e21b815273d51a44d3fae010294c616388b506acda1bfaae46600482015273c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2602482015273dac17f958d2ee523a2206206994597c13d831ec76044820152606481018290526000907381c46feca27b31f3adc2b91ee4be9717d1cd3dd790633973e83490608401602060405180830381865afa158015610c7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca39190612c0c565b60fb54604051630e5cfa0d60e21b815273ed279fdd11ca84beef15af5d39bb4d4bee23f0ca600482015273dac17f958d2ee523a2206206994597c13d831ec760248201526001600160a01b039091166044820152606481018290529091506000907381c46feca27b31f3adc2b91ee4be9717d1cd3dd790633973e83490608401602060405180830381865afa158015610d40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d649190612c0c565b60fd54604051621cdc4760e81b815230600482015291925082916001600160a01b0390911690631cdc470090602401602060405180830381865afa158015610db0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd49190612c0c565b610dde9190612c3b565b935050505090565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161415610e745760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b19195b1959d85d1958d85b1b60a21b60648201526084016108df565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610ecf7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b031614610f3a5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b6163746976652070726f787960a01b60648201526084016108df565b610f4382611e28565b6108f282826001611e77565b6000306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610fef5760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c000000000000000060648201526084016108df565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b600061101e610ad2565b1515919050565b3360009081527fa54cab3e0e5c813b18c71e4e96696bc5fad68ff5192a677568a6b075ee26500e602052604090205460ff1661108d576040517f81389f2000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61109688611cb9565b6001600160a01b038816600090815260ff6020819052604090912054166110e9576040517f9e98954000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110f587898888612017565b6111018489858561221b565b60fb546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa15801561114a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061116e9190612c0c565b9050806111a7576040517f66a9132900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818110156111e1576040517ff0b5161200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518181527fe2cd27e14f657c20a75ca97eb4b59f450a055c24916d27034e78933b8c7d75639060200160405180910390a160fd54604051635f788d6560e01b815260048101839052600060248201526001600160a01b0390911690635f788d6590604401600060405180830381600087803b15801561126157600080fd5b505af1158015611275573d6000803e3d6000fd5b50505050505050505050505050565b3360009081527f81fe90a866a48a634a12852c1be675b683a22307409932a7443b8029347be756602052604090205460ff166112d3576040516378741b4560e11b815260040160405180910390fd5b6001600160a01b0381166112fa57604051638f6e893360e01b815260040160405180910390fd5b6001600160a01b03811633141561133d576040517f8fbf788d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611348600082611d03565b6113727ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab82611d03565b61139c7ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309582611d03565b6113a7600033611da5565b6113d17ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab33611da5565b610a6f7ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309533611da5565b600054610100900460ff161580801561141b5750600054600160ff909116105b806114355750303b158015611435575060005460ff166001145b6114a75760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108df565b6000805460ff1916600117905580156114ca576000805461ff0019166101001790555b6114d2612305565b6114da612305565b6001600160a01b03861661150157604051638f6e893360e01b815260040160405180910390fd5b6001600160a01b038416611541576040517f96fbf73100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516611581576040517f0f44c88000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0383166115c1576040517f4043fac000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115f46001600160a01b0388167f21d982ed00000000000000000000000000000000000000000000000000000000612384565b61162a576040517fd7e681d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821661166a576040517f39f97e6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611675600087611d03565b61169f7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab87611d03565b6116c97ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309587611d03565b6116f37f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0888611d03565b61171d7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab83611d03565b60fc80546001600160a01b0389811673ffffffffffffffffffffffffffffffffffffffff199283161790925560fb8054868416908316811790915560fd8054898516908416811790915560fe8054948916949093169390931790915560405163095ea7b360e01b8152600481019290925260001960248301529063095ea7b3906044016020604051808303816000875af11580156117bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e39190612c53565b61180b576040516351be55a360e11b81526001600160a01b03841660048201526024016108df565b8015611851576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b3360009081527ff5f43b48833b613af61a2aa61573d51591ce86ef304bb6e01e9115b2a39191de602052604090205460ff166118a957604051634cc2cd3760e11b815260040160405180910390fd5b806118e0576040517fd45ab8b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118e8610ad2565b811115611921576040517f8396942200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60fd54604051632e54bf9560e01b8152600481018390526001600160a01b0390911690632e54bf9590602401600060405180830381600087803b15801561196757600080fd5b505af115801561197b573d6000803e3d6000fd5b505060fb546040516370a0823160e01b8152306004820152600093506001600160a01b0390911691506370a0823190602401602060405180830381865afa1580156119ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ee9190612c0c565b60fb5460fc546040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b03918216600482015260248101849052929350169063a9059cbb906044016020604051808303816000875af1158015611a5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a829190612c53565b611ac75760fb546040517f4dca0c840000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016108df565b6040518181527f35c4c11317fd96ffa30da51e0a4b85219c4b0f56c1507c3afefe79de0c47ef549060200160405180910390a15050565b600082815260c96020526040902060010154611b1981611cf9565b6108608383611da5565b3360009081527ff5f43b48833b613af61a2aa61573d51591ce86ef304bb6e01e9115b2a39191de602052604090205460ff16611b7257604051634cc2cd3760e11b815260040160405180910390fd5b60fb546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015611bbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bdf9190612c0c565b905080611c18576040517f378a16c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60fd54604051635f788d6560e01b815260048101839052600060248201526001600160a01b0390911690635f788d6590604401600060405180830381600087803b158015611c6557600080fd5b505af1158015611c79573d6000803e3d6000fd5b505050507fc68748140896ba027a48ebf3a24735d0afb4ab835f92c4815ddb34f9d6dfd18a81604051611cae91815260200190565b60405180910390a150565b6001600160a01b038116610a6f576040517f99c4312400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a6f8133612426565b600082815260c9602090815260408083206001600160a01b038516845290915290205460ff166108f257600082815260c9602090815260408083206001600160a01b03851684529091529020805460ff19166001179055611d613390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600082815260c9602090815260408083206001600160a01b038516845290915290205460ff16156108f257600082815260c9602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b3360009081527f81fe90a866a48a634a12852c1be675b683a22307409932a7443b8029347be756602052604090205460ff16610a6f576040516378741b4560e11b815260040160405180910390fd5b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615611eaa57610860836124a6565b826001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611f04575060408051601f3d908101601f19168201909252611f0191810190612c0c565b60015b611f765760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f74205555505300000000000000000000000000000000000060648201526084016108df565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc811461200b5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c6555554944000000000000000000000000000000000000000000000060648201526084016108df565b50610860838383612571565b831580612022575080155b1561202c57610acc565b60fe546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015612075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120999190612c0c565b9050808511156120d5576040517f19736e2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60fe5460405163095ea7b360e01b81526001600160a01b038681166004830152602482018890529091169063095ea7b3906044016020604051808303816000875af1158015612128573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214c9190612c53565b6121785760fe546040516351be55a360e11b81526001600160a01b0390911660048201526024016108df565b6000846001600160a01b031660008585604051612196929190612c75565b60006040518083038185875af1925050503d80600081146121d3576040519150601f19603f3d011682016040523d82523d6000602084013e6121d8565b606091505b5050905080612213576040517f56a3cf9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b831580612226575080155b1561223057610acc565b478085111561226b576040517f0d38adfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000846001600160a01b0316868585604051612288929190612c75565b60006040518083038185875af1925050503d80600081146122c5576040519150601f19603f3d011682016040523d82523d6000602084013e6122ca565b606091505b5050905080612213576040517f257745cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600054610100900460ff166123825760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108df565b565b60008080612399856301ffc9a760e01b612596565b90925090508115806123a9575080155b156123b957600092505050610740565b6123cb856001600160e01b0319612596565b90925090508115806123dc57508015155b156123ec57600092505050610740565b6123f68585612596565b909250905060018214801561240b5750806001145b1561241b57600192505050610740565b506000949350505050565b600082815260c9602090815260408083206001600160a01b038516845290915290205460ff166108f257612464816001600160a01b031660146125ca565b61246f8360206125ca565b604051602001612480929190612cb1565b60408051601f198184030181529082905262461bcd60e51b82526108df91600401612d32565b6001600160a01b0381163b6125235760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e74726163740000000000000000000000000000000000000060648201526084016108df565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b61257a836127b2565b6000825111806125875750805b1561086057610acc83836127f2565b6040516301ffc9a760e01b8082526004820183905260009182919060208160248189617530fa905190969095509350505050565b606060006125d9836002612d65565b6125e4906002612c3b565b67ffffffffffffffff8111156125fc576125fc6129dc565b6040519080825280601f01601f191660200182016040528015612626576020820181803683370190505b5090507f30000000000000000000000000000000000000000000000000000000000000008160008151811061265d5761265d612d84565b60200101906001600160f81b031916908160001a9053507f7800000000000000000000000000000000000000000000000000000000000000816001815181106126a8576126a8612d84565b60200101906001600160f81b031916908160001a90535060006126cc846002612d65565b6126d7906001612c3b565b90505b600181111561275c577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061271857612718612d84565b1a60f81b82828151811061272e5761272e612d84565b60200101906001600160f81b031916908160001a90535060049490941c9361275581612d9a565b90506126da565b5083156127ab5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016108df565b9392505050565b6127bb816124a6565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606001600160a01b0383163b6128715760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016108df565b600080846001600160a01b03168460405161288c9190612db1565b600060405180830381855af49150503d80600081146128c7576040519150601f19603f3d011682016040523d82523d6000602084013e6128cc565b606091505b50915091506128f48282604051806060016040528060278152602001612dce602791396128fd565b95945050505050565b6060831561290c5750816127ab565b82511561291c5782518084602001fd5b8160405162461bcd60e51b81526004016108df9190612d32565b60006020828403121561294857600080fd5b81356001600160e01b0319811681146127ab57600080fd5b80356001600160a01b038116811461297757600080fd5b919050565b60006020828403121561298e57600080fd5b6127ab82612960565b6000602082840312156129a957600080fd5b5035919050565b600080604083850312156129c357600080fd5b823591506129d360208401612960565b90509250929050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215612a0557600080fd5b612a0e83612960565b9150602083013567ffffffffffffffff80821115612a2b57600080fd5b818501915085601f830112612a3f57600080fd5b813581811115612a5157612a516129dc565b604051601f8201601f19908116603f01168101908382118183101715612a7957612a796129dc565b81604052828152886020848701011115612a9257600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60008083601f840112612ac657600080fd5b50813567ffffffffffffffff811115612ade57600080fd5b602083019150836020828501011115612af657600080fd5b9250929050565b60008060008060008060008060c0898b031215612b1957600080fd5b612b2289612960565b975060208901359650604089013567ffffffffffffffff80821115612b4657600080fd5b612b528c838d01612ab4565b909850965060608b0135955060808b0135915080821115612b7257600080fd5b50612b7f8b828c01612ab4565b999c989b50969995989497949560a00135949350505050565b60008060008060008060c08789031215612bb157600080fd5b612bba87612960565b9550612bc860208801612960565b9450612bd660408801612960565b9350612be460608801612960565b9250612bf260808801612960565b9150612c0060a08801612960565b90509295509295509295565b600060208284031215612c1e57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b60008219821115612c4e57612c4e612c25565b500190565b600060208284031215612c6557600080fd5b815180151581146127ab57600080fd5b8183823760009101908152919050565b60005b83811015612ca0578181015183820152602001612c88565b83811115610acc5750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351612ce9816017850160208801612c85565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351612d26816028840160208801612c85565b01602801949350505050565b6020815260008251806020840152612d51816040850160208701612c85565b601f01601f19169190910160400192915050565b6000816000190483118215151615612d7f57612d7f612c25565b500290565b634e487b7160e01b600052603260045260246000fd5b600081612da957612da9612c25565b506000190190565b60008251612dc3818460208701612c85565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122066c5c3d1ef7b4e6a24daa041e52a599abc270b844d5e5e489cff888d95783c4a64736f6c634300080a0033

Deployed Bytecode

0x60806040526004361061021d5760003560e01c80636ff902e71161011d578063c54e44eb116100b0578063e49b30da1161007f578063ec87621c11610064578063ec87621c1461066e578063f6289887146106a2578063fbfa77cf146106d657600080fd5b8063e49b30da14610631578063e8b5e51f1461065957600080fd5b8063c54e44eb146105a9578063cc2a9a5b146105d1578063ce8c42e8146105f1578063d547741f1461061157600080fd5b80639af2e635116100ec5780639af2e63514610538578063a217fddf1461054c578063ad5c464814610561578063b5106add1461058957600080fd5b80636ff902e71461048a5780637f6ec455146104b2578063865a3e47146104d257806391d14854146104f257600080fd5b8063364bc15a116101b05780634ad0b6841161017f57806352d1902d1161016457806352d1902d146104405780635be9b2d3146104555780636f307dc31461046a57600080fd5b80634ad0b684146104185780634f1ef2861461042d57600080fd5b8063364bc15a1461038f57806336568abe146103c35780633659cfe6146103e35780634641257d1461040357600080fd5b806321bc5df4116101ec57806321bc5df4146102e9578063248a9ca3146103095780632f2ff15d1461034757806330d8a9e81461036757600080fd5b806301ffc9a714610229578063048c661d1461025e578063129d91ab146102965780631fa1fe36146102b857600080fd5b3661022457005b600080fd5b34801561023557600080fd5b50610249610244366004612936565b6106f6565b60405190151581526020015b60405180910390f35b34801561026a57600080fd5b5060fd5461027e906001600160a01b031681565b6040516001600160a01b039091168152602001610255565b3480156102a257600080fd5b506102b66102b136600461297c565b610746565b005b3480156102c457600080fd5b506102496102d336600461297c565b60ff602081905260009182526040909120541681565b3480156102f557600080fd5b506102b661030436600461297c565b6107bf565b34801561031557600080fd5b50610339610324366004612997565b600090815260c9602052604090206001015490565b604051908152602001610255565b34801561035357600080fd5b506102b66103623660046129b0565b61083b565b34801561037357600080fd5b5061027e73ed279fdd11ca84beef15af5d39bb4d4bee23f0ca81565b34801561039b57600080fd5b506103397ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b3480156103cf57600080fd5b506102b66103de3660046129b0565b610865565b3480156103ef57600080fd5b506102b66103fe36600461297c565b6108f6565b34801561040f57600080fd5b506102b6610a72565b34801561042457600080fd5b50610339610ad2565b6102b661043b3660046129f2565b610de6565b34801561044c57600080fd5b50610339610f4f565b34801561046157600080fd5b50610249611014565b34801561047657600080fd5b5060fb5461027e906001600160a01b031681565b34801561049657600080fd5b5061027e73d51a44d3fae010294c616388b506acda1bfaae4681565b3480156104be57600080fd5b5060fe5461027e906001600160a01b031681565b3480156104de57600080fd5b506102b66104ed366004612afd565b611025565b3480156104fe57600080fd5b5061024961050d3660046129b0565b600091825260c9602090815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561054457600080fd5b506001610249565b34801561055857600080fd5b50610339600081565b34801561056d57600080fd5b5061027e73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b34801561059557600080fd5b506102b66105a436600461297c565b611284565b3480156105b557600080fd5b5061027e73dac17f958d2ee523a2206206994597c13d831ec781565b3480156105dd57600080fd5b506102b66105ec366004612b98565b6113fb565b3480156105fd57600080fd5b506102b661060c366004612997565b61185a565b34801561061d57600080fd5b506102b661062c3660046129b0565b611afe565b34801561063d57600080fd5b5061027e7381c46feca27b31f3adc2b91ee4be9717d1cd3dd781565b34801561066557600080fd5b506102b6611b23565b34801561067a57600080fd5b506103397f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0881565b3480156106ae57600080fd5b506103397ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309581565b3480156106e257600080fd5b5060fc5461027e906001600160a01b031681565b60006001600160e01b031982167f7965db0b00000000000000000000000000000000000000000000000000000000148061074057506301ffc9a760e01b6001600160e01b03198316145b92915050565b3360009081527fdf9e05033a6c4e281e51ff7ac61d3a2fec43ac6ee7b61abf4c8074f4927147b7602052604090205460ff16610795576040516370c4ee8d60e01b815260040160405180910390fd5b61079e81611cb9565b6001600160a01b0316600090815260ff60205260409020805460ff19169055565b3360009081527fdf9e05033a6c4e281e51ff7ac61d3a2fec43ac6ee7b61abf4c8074f4927147b7602052604090205460ff1661080e576040516370c4ee8d60e01b815260040160405180910390fd5b61081781611cb9565b6001600160a01b0316600090815260ff60205260409020805460ff19166001179055565b600082815260c9602052604090206001015461085681611cf9565b6108608383611d03565b505050565b6001600160a01b03811633146108e85760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6108f28282611da5565b5050565b306001600160a01b037f000000000000000000000000537ee18ca78c42e1e7fb87fecfe9fc811c3d35751614156109845760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b19195b1959d85d1958d85b1b60a21b60648201526084016108df565b7f000000000000000000000000537ee18ca78c42e1e7fb87fecfe9fc811c3d35756001600160a01b03166109df7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b031614610a4a5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b6163746976652070726f787960a01b60648201526084016108df565b610a5381611e28565b60408051600080825260208201909252610a6f91839190611e77565b50565b60fd54604051632e54bf9560e01b8152600060048201526001600160a01b0390911690632e54bf9590602401600060405180830381600087803b158015610ab857600080fd5b505af1158015610acc573d6000803e3d6000fd5b50505050565b60fd546040517f389e92a500000000000000000000000000000000000000000000000000000000815230600482015260009182916001600160a01b039091169063389e92a590602401602060405180830381865afa158015610b38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b5c9190612c0c565b610b669047612c3b565b905080610bde5760fd54604051621cdc4760e81b81523060048201526001600160a01b0390911690631cdc470090602401602060405180830381865afa158015610bb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd89190612c0c565b91505090565b604051630e5cfa0d60e21b815273d51a44d3fae010294c616388b506acda1bfaae46600482015273c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2602482015273dac17f958d2ee523a2206206994597c13d831ec76044820152606481018290526000907381c46feca27b31f3adc2b91ee4be9717d1cd3dd790633973e83490608401602060405180830381865afa158015610c7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca39190612c0c565b60fb54604051630e5cfa0d60e21b815273ed279fdd11ca84beef15af5d39bb4d4bee23f0ca600482015273dac17f958d2ee523a2206206994597c13d831ec760248201526001600160a01b039091166044820152606481018290529091506000907381c46feca27b31f3adc2b91ee4be9717d1cd3dd790633973e83490608401602060405180830381865afa158015610d40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d649190612c0c565b60fd54604051621cdc4760e81b815230600482015291925082916001600160a01b0390911690631cdc470090602401602060405180830381865afa158015610db0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd49190612c0c565b610dde9190612c3b565b935050505090565b306001600160a01b037f000000000000000000000000537ee18ca78c42e1e7fb87fecfe9fc811c3d3575161415610e745760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b19195b1959d85d1958d85b1b60a21b60648201526084016108df565b7f000000000000000000000000537ee18ca78c42e1e7fb87fecfe9fc811c3d35756001600160a01b0316610ecf7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b031614610f3a5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b6163746976652070726f787960a01b60648201526084016108df565b610f4382611e28565b6108f282826001611e77565b6000306001600160a01b037f000000000000000000000000537ee18ca78c42e1e7fb87fecfe9fc811c3d35751614610fef5760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c000000000000000060648201526084016108df565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b600061101e610ad2565b1515919050565b3360009081527fa54cab3e0e5c813b18c71e4e96696bc5fad68ff5192a677568a6b075ee26500e602052604090205460ff1661108d576040517f81389f2000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61109688611cb9565b6001600160a01b038816600090815260ff6020819052604090912054166110e9576040517f9e98954000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110f587898888612017565b6111018489858561221b565b60fb546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa15801561114a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061116e9190612c0c565b9050806111a7576040517f66a9132900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818110156111e1576040517ff0b5161200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518181527fe2cd27e14f657c20a75ca97eb4b59f450a055c24916d27034e78933b8c7d75639060200160405180910390a160fd54604051635f788d6560e01b815260048101839052600060248201526001600160a01b0390911690635f788d6590604401600060405180830381600087803b15801561126157600080fd5b505af1158015611275573d6000803e3d6000fd5b50505050505050505050505050565b3360009081527f81fe90a866a48a634a12852c1be675b683a22307409932a7443b8029347be756602052604090205460ff166112d3576040516378741b4560e11b815260040160405180910390fd5b6001600160a01b0381166112fa57604051638f6e893360e01b815260040160405180910390fd5b6001600160a01b03811633141561133d576040517f8fbf788d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611348600082611d03565b6113727ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab82611d03565b61139c7ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309582611d03565b6113a7600033611da5565b6113d17ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab33611da5565b610a6f7ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309533611da5565b600054610100900460ff161580801561141b5750600054600160ff909116105b806114355750303b158015611435575060005460ff166001145b6114a75760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108df565b6000805460ff1916600117905580156114ca576000805461ff0019166101001790555b6114d2612305565b6114da612305565b6001600160a01b03861661150157604051638f6e893360e01b815260040160405180910390fd5b6001600160a01b038416611541576040517f96fbf73100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516611581576040517f0f44c88000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0383166115c1576040517f4043fac000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115f46001600160a01b0388167f21d982ed00000000000000000000000000000000000000000000000000000000612384565b61162a576040517fd7e681d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821661166a576040517f39f97e6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611675600087611d03565b61169f7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab87611d03565b6116c97ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309587611d03565b6116f37f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0888611d03565b61171d7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab83611d03565b60fc80546001600160a01b0389811673ffffffffffffffffffffffffffffffffffffffff199283161790925560fb8054868416908316811790915560fd8054898516908416811790915560fe8054948916949093169390931790915560405163095ea7b360e01b8152600481019290925260001960248301529063095ea7b3906044016020604051808303816000875af11580156117bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e39190612c53565b61180b576040516351be55a360e11b81526001600160a01b03841660048201526024016108df565b8015611851576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b3360009081527ff5f43b48833b613af61a2aa61573d51591ce86ef304bb6e01e9115b2a39191de602052604090205460ff166118a957604051634cc2cd3760e11b815260040160405180910390fd5b806118e0576040517fd45ab8b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118e8610ad2565b811115611921576040517f8396942200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60fd54604051632e54bf9560e01b8152600481018390526001600160a01b0390911690632e54bf9590602401600060405180830381600087803b15801561196757600080fd5b505af115801561197b573d6000803e3d6000fd5b505060fb546040516370a0823160e01b8152306004820152600093506001600160a01b0390911691506370a0823190602401602060405180830381865afa1580156119ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ee9190612c0c565b60fb5460fc546040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b03918216600482015260248101849052929350169063a9059cbb906044016020604051808303816000875af1158015611a5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a829190612c53565b611ac75760fb546040517f4dca0c840000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016108df565b6040518181527f35c4c11317fd96ffa30da51e0a4b85219c4b0f56c1507c3afefe79de0c47ef549060200160405180910390a15050565b600082815260c96020526040902060010154611b1981611cf9565b6108608383611da5565b3360009081527ff5f43b48833b613af61a2aa61573d51591ce86ef304bb6e01e9115b2a39191de602052604090205460ff16611b7257604051634cc2cd3760e11b815260040160405180910390fd5b60fb546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015611bbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bdf9190612c0c565b905080611c18576040517f378a16c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60fd54604051635f788d6560e01b815260048101839052600060248201526001600160a01b0390911690635f788d6590604401600060405180830381600087803b158015611c6557600080fd5b505af1158015611c79573d6000803e3d6000fd5b505050507fc68748140896ba027a48ebf3a24735d0afb4ab835f92c4815ddb34f9d6dfd18a81604051611cae91815260200190565b60405180910390a150565b6001600160a01b038116610a6f576040517f99c4312400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a6f8133612426565b600082815260c9602090815260408083206001600160a01b038516845290915290205460ff166108f257600082815260c9602090815260408083206001600160a01b03851684529091529020805460ff19166001179055611d613390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600082815260c9602090815260408083206001600160a01b038516845290915290205460ff16156108f257600082815260c9602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b3360009081527f81fe90a866a48a634a12852c1be675b683a22307409932a7443b8029347be756602052604090205460ff16610a6f576040516378741b4560e11b815260040160405180910390fd5b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615611eaa57610860836124a6565b826001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611f04575060408051601f3d908101601f19168201909252611f0191810190612c0c565b60015b611f765760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f74205555505300000000000000000000000000000000000060648201526084016108df565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc811461200b5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c6555554944000000000000000000000000000000000000000000000060648201526084016108df565b50610860838383612571565b831580612022575080155b1561202c57610acc565b60fe546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015612075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120999190612c0c565b9050808511156120d5576040517f19736e2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60fe5460405163095ea7b360e01b81526001600160a01b038681166004830152602482018890529091169063095ea7b3906044016020604051808303816000875af1158015612128573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214c9190612c53565b6121785760fe546040516351be55a360e11b81526001600160a01b0390911660048201526024016108df565b6000846001600160a01b031660008585604051612196929190612c75565b60006040518083038185875af1925050503d80600081146121d3576040519150601f19603f3d011682016040523d82523d6000602084013e6121d8565b606091505b5050905080612213576040517f56a3cf9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b831580612226575080155b1561223057610acc565b478085111561226b576040517f0d38adfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000846001600160a01b0316868585604051612288929190612c75565b60006040518083038185875af1925050503d80600081146122c5576040519150601f19603f3d011682016040523d82523d6000602084013e6122ca565b606091505b5050905080612213576040517f257745cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600054610100900460ff166123825760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108df565b565b60008080612399856301ffc9a760e01b612596565b90925090508115806123a9575080155b156123b957600092505050610740565b6123cb856001600160e01b0319612596565b90925090508115806123dc57508015155b156123ec57600092505050610740565b6123f68585612596565b909250905060018214801561240b5750806001145b1561241b57600192505050610740565b506000949350505050565b600082815260c9602090815260408083206001600160a01b038516845290915290205460ff166108f257612464816001600160a01b031660146125ca565b61246f8360206125ca565b604051602001612480929190612cb1565b60408051601f198184030181529082905262461bcd60e51b82526108df91600401612d32565b6001600160a01b0381163b6125235760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e74726163740000000000000000000000000000000000000060648201526084016108df565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b61257a836127b2565b6000825111806125875750805b1561086057610acc83836127f2565b6040516301ffc9a760e01b8082526004820183905260009182919060208160248189617530fa905190969095509350505050565b606060006125d9836002612d65565b6125e4906002612c3b565b67ffffffffffffffff8111156125fc576125fc6129dc565b6040519080825280601f01601f191660200182016040528015612626576020820181803683370190505b5090507f30000000000000000000000000000000000000000000000000000000000000008160008151811061265d5761265d612d84565b60200101906001600160f81b031916908160001a9053507f7800000000000000000000000000000000000000000000000000000000000000816001815181106126a8576126a8612d84565b60200101906001600160f81b031916908160001a90535060006126cc846002612d65565b6126d7906001612c3b565b90505b600181111561275c577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061271857612718612d84565b1a60f81b82828151811061272e5761272e612d84565b60200101906001600160f81b031916908160001a90535060049490941c9361275581612d9a565b90506126da565b5083156127ab5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016108df565b9392505050565b6127bb816124a6565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606001600160a01b0383163b6128715760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016108df565b600080846001600160a01b03168460405161288c9190612db1565b600060405180830381855af49150503d80600081146128c7576040519150601f19603f3d011682016040523d82523d6000602084013e6128cc565b606091505b50915091506128f48282604051806060016040528060278152602001612dce602791396128fd565b95945050505050565b6060831561290c5750816127ab565b82511561291c5782518084602001fd5b8160405162461bcd60e51b81526004016108df9190612d32565b60006020828403121561294857600080fd5b81356001600160e01b0319811681146127ab57600080fd5b80356001600160a01b038116811461297757600080fd5b919050565b60006020828403121561298e57600080fd5b6127ab82612960565b6000602082840312156129a957600080fd5b5035919050565b600080604083850312156129c357600080fd5b823591506129d360208401612960565b90509250929050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215612a0557600080fd5b612a0e83612960565b9150602083013567ffffffffffffffff80821115612a2b57600080fd5b818501915085601f830112612a3f57600080fd5b813581811115612a5157612a516129dc565b604051601f8201601f19908116603f01168101908382118183101715612a7957612a796129dc565b81604052828152886020848701011115612a9257600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60008083601f840112612ac657600080fd5b50813567ffffffffffffffff811115612ade57600080fd5b602083019150836020828501011115612af657600080fd5b9250929050565b60008060008060008060008060c0898b031215612b1957600080fd5b612b2289612960565b975060208901359650604089013567ffffffffffffffff80821115612b4657600080fd5b612b528c838d01612ab4565b909850965060608b0135955060808b0135915080821115612b7257600080fd5b50612b7f8b828c01612ab4565b999c989b50969995989497949560a00135949350505050565b60008060008060008060c08789031215612bb157600080fd5b612bba87612960565b9550612bc860208801612960565b9450612bd660408801612960565b9350612be460608801612960565b9250612bf260808801612960565b9150612c0060a08801612960565b90509295509295509295565b600060208284031215612c1e57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b60008219821115612c4e57612c4e612c25565b500190565b600060208284031215612c6557600080fd5b815180151581146127ab57600080fd5b8183823760009101908152919050565b60005b83811015612ca0578181015183820152602001612c88565b83811115610acc5750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351612ce9816017850160208801612c85565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351612d26816028840160208801612c85565b01602801949350505050565b6020815260008251806020840152612d51816040850160208701612c85565b601f01601f19169190910160400192915050565b6000816000190483118215151615612d7f57612d7f612c25565b500290565b634e487b7160e01b600052603260045260246000fd5b600081612da957612da9612c25565b506000190190565b60008251612dc3818460208701612c85565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122066c5c3d1ef7b4e6a24daa041e52a599abc270b844d5e5e489cff888d95783c4a64736f6c634300080a0033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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