ETH Price: $2,636.98 (+1.44%)

Contract

0x86BfC81C6B35AbaE8Dd48632Ee5bCE4c5C643412
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Deploy143133652022-03-03 10:00:201078 days ago1646301620IN
0x86BfC81C...c5C643412
0 ETH0.013912126.32265296
Deploy143133642022-03-03 10:00:181078 days ago1646301618IN
0x86BfC81C...c5C643412
0 ETH0.0132896526.99700309
Deploy143133642022-03-03 10:00:181078 days ago1646301618IN
0x86BfC81C...c5C643412
0 ETH0.0142044426.99700309
Deploy143133642022-03-03 10:00:181078 days ago1646301618IN
0x86BfC81C...c5C643412
0 ETH0.0142044426.99700309
Deploy143133642022-03-03 10:00:181078 days ago1646301618IN
0x86BfC81C...c5C643412
0 ETH0.0134233926.99700309
Deploy143133642022-03-03 10:00:181078 days ago1646301618IN
0x86BfC81C...c5C643412
0 ETH0.0134233926.99700309
Deploy143133642022-03-03 10:00:181078 days ago1646301618IN
0x86BfC81C...c5C643412
0 ETH0.0134233926.99700309
Deploy143133642022-03-03 10:00:181078 days ago1646301618IN
0x86BfC81C...c5C643412
0 ETH0.0124413226.99700309
Deploy143133602022-03-03 9:59:311078 days ago1646301571IN
0x86BfC81C...c5C643412
0 ETH0.0115210324.86137156
Deploy143133602022-03-03 9:59:311078 days ago1646301571IN
0x86BfC81C...c5C643412
0 ETH0.0115210324.86137156
Deploy143133562022-03-03 9:59:141078 days ago1646301554IN
0x86BfC81C...c5C643412
0 ETH0.0119971125.88871739
Deploy143133562022-03-03 9:59:141078 days ago1646301554IN
0x86BfC81C...c5C643412
0 ETH0.0119971125.88871739
Deploy143133552022-03-03 9:59:081078 days ago1646301548IN
0x86BfC81C...c5C643412
0 ETH0.0125401627.05444379
Deploy143133552022-03-03 9:59:081078 days ago1646301548IN
0x86BfC81C...c5C643412
0 ETH0.0125401627.05444379
Deploy143133552022-03-03 9:59:081078 days ago1646301548IN
0x86BfC81C...c5C643412
0 ETH0.0134547727.05444379
Deploy143133552022-03-03 9:59:081078 days ago1646301548IN
0x86BfC81C...c5C643412
0 ETH0.0125381627.05444379
Deploy143133552022-03-03 9:59:081078 days ago1646301548IN
0x86BfC81C...c5C643412
0 ETH0.0134547727.05444379
Deploy143133552022-03-03 9:59:081078 days ago1646301548IN
0x86BfC81C...c5C643412
0 ETH0.0125381627.05444379
Deploy143133522022-03-03 9:58:221078 days ago1646301502IN
0x86BfC81C...c5C643412
0 ETH0.0123797927.91197196

Latest 20 internal transactions

Advanced mode:
Parent Transaction Hash Block
From
To
143133652022-03-03 10:00:201078 days ago1646301620
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133642022-03-03 10:00:181078 days ago1646301618
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133642022-03-03 10:00:181078 days ago1646301618
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133642022-03-03 10:00:181078 days ago1646301618
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133642022-03-03 10:00:181078 days ago1646301618
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133642022-03-03 10:00:181078 days ago1646301618
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133642022-03-03 10:00:181078 days ago1646301618
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133642022-03-03 10:00:181078 days ago1646301618
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133602022-03-03 9:59:311078 days ago1646301571
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133602022-03-03 9:59:311078 days ago1646301571
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133562022-03-03 9:59:141078 days ago1646301554
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133562022-03-03 9:59:141078 days ago1646301554
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133552022-03-03 9:59:081078 days ago1646301548
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133552022-03-03 9:59:081078 days ago1646301548
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133552022-03-03 9:59:081078 days ago1646301548
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133552022-03-03 9:59:081078 days ago1646301548
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133552022-03-03 9:59:081078 days ago1646301548
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133552022-03-03 9:59:081078 days ago1646301548
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143133522022-03-03 9:58:221078 days ago1646301502
0x86BfC81C...c5C643412
 Contract Creation0 ETH
143125092022-03-03 6:54:501078 days ago1646290490
0x86BfC81C...c5C643412
 Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ConvexCurveLpStakingWrapperFactory

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion, GNU GPLv3 license
File 1 of 21 : ConvexCurveLpStakingWrapperFactory.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.
    (c) Enzyme Council <[email protected]>
    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "../../../../persistent/dispatcher/IDispatcher.sol";
import "../../../utils/beacon-proxy/BeaconProxyFactory.sol";
import "./ConvexCurveLpStakingWrapperLib.sol";

/// @title ConvexCurveLpStakingWrapperFactory Contract
/// @author Enzyme Council <[email protected]>
/// @notice A contract factory for ConvexCurveLpStakingWrapper instances
contract ConvexCurveLpStakingWrapperFactory is BeaconProxyFactory {
    event WrapperDeployed(uint256 indexed pid, address wrapperProxy, address curveLpToken);

    IDispatcher private immutable DISPATCHER_CONTRACT;

    mapping(uint256 => address) private pidToWrapper;
    // Handy cache for interacting contracts
    mapping(address => address) private wrapperToCurveLpToken;

    modifier onlyOwner {
        require(msg.sender == getOwner(), "Only the owner can call this function");
        _;
    }

    constructor(
        address _dispatcher,
        address _convexBooster,
        address _crvToken,
        address _cvxToken
    ) public BeaconProxyFactory(address(0)) {
        DISPATCHER_CONTRACT = IDispatcher(_dispatcher);

        __setCanonicalLib(
            address(
                new ConvexCurveLpStakingWrapperLib(
                    address(this),
                    _convexBooster,
                    _crvToken,
                    _cvxToken
                )
            )
        );
    }

    /// @notice Deploys a staking wrapper for a given Convex pool
    /// @param _pid The Convex Curve pool id
    /// @return wrapperProxy_ The staking wrapper proxy contract address
    function deploy(uint256 _pid) external returns (address wrapperProxy_) {
        require(getWrapperForConvexPool(_pid) == address(0), "deploy: Wrapper already exists");

        bytes memory constructData = abi.encodeWithSelector(
            ConvexCurveLpStakingWrapperLib.init.selector,
            _pid
        );

        wrapperProxy_ = deployProxy(constructData);

        pidToWrapper[_pid] = wrapperProxy_;

        address lpToken = ConvexCurveLpStakingWrapperLib(wrapperProxy_).getCurveLpToken();
        wrapperToCurveLpToken[wrapperProxy_] = lpToken;

        emit WrapperDeployed(_pid, wrapperProxy_, lpToken);

        return wrapperProxy_;
    }

    /// @notice Pause deposits and harvesting new rewards for the given wrappers
    /// @param _wrappers The wrappers to pause
    function pauseWrappers(address[] calldata _wrappers) external onlyOwner {
        for (uint256 i; i < _wrappers.length; i++) {
            ConvexCurveLpStakingWrapperLib(_wrappers[i]).togglePause(true);
        }
    }

    /// @notice Unpauses deposits and harvesting new rewards for the given wrappers
    /// @param _wrappers The wrappers to unpause
    function unpauseWrappers(address[] calldata _wrappers) external onlyOwner {
        for (uint256 i; i < _wrappers.length; i++) {
            ConvexCurveLpStakingWrapperLib(_wrappers[i]).togglePause(false);
        }
    }

    ////////////////////////////////////
    // BEACON PROXY FACTORY OVERRIDES //
    ////////////////////////////////////

    /// @notice Gets the contract owner
    /// @return owner_ The contract owner
    function getOwner() public view override returns (address owner_) {
        return DISPATCHER_CONTRACT.getOwner();
    }

    ///////////////////
    // STATE GETTERS //
    ///////////////////

    // EXTERNAL FUNCTIONS

    /// @notice Gets the Curve LP token address for a given wrapper
    /// @param _wrapper The wrapper proxy address
    /// @return lpToken_ The Curve LP token address
    function getCurveLpTokenForWrapper(address _wrapper) external view returns (address lpToken_) {
        return wrapperToCurveLpToken[_wrapper];
    }

    // PUBLIC FUNCTIONS

    /// @notice Gets the wrapper address for a given Convex pool
    /// @param _pid The Convex pool id
    /// @return wrapper_ The wrapper proxy address
    function getWrapperForConvexPool(uint256 _pid) public view returns (address wrapper_) {
        return pidToWrapper[_pid];
    }
}

File 2 of 21 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

File 3 of 21 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) public {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}

File 4 of 21 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);

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

File 5 of 21 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 6 of 21 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 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");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

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

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

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 7 of 21 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

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

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 8 of 21 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor () internal {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 9 of 21 : IDispatcher.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.

    (c) Enzyme Council <[email protected]>

    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;

/// @title IDispatcher Interface
/// @author Enzyme Council <[email protected]>
interface IDispatcher {
    function cancelMigration(address _vaultProxy, bool _bypassFailure) external;

    function claimOwnership() external;

    function deployVaultProxy(
        address _vaultLib,
        address _owner,
        address _vaultAccessor,
        string calldata _fundName
    ) external returns (address vaultProxy_);

    function executeMigration(address _vaultProxy, bool _bypassFailure) external;

    function getCurrentFundDeployer() external view returns (address currentFundDeployer_);

    function getFundDeployerForVaultProxy(address _vaultProxy)
        external
        view
        returns (address fundDeployer_);

    function getMigrationRequestDetailsForVaultProxy(address _vaultProxy)
        external
        view
        returns (
            address nextFundDeployer_,
            address nextVaultAccessor_,
            address nextVaultLib_,
            uint256 executableTimestamp_
        );

    function getMigrationTimelock() external view returns (uint256 migrationTimelock_);

    function getNominatedOwner() external view returns (address nominatedOwner_);

    function getOwner() external view returns (address owner_);

    function getSharesTokenSymbol() external view returns (string memory sharesTokenSymbol_);

    function getTimelockRemainingForMigrationRequest(address _vaultProxy)
        external
        view
        returns (uint256 secondsRemaining_);

    function hasExecutableMigrationRequest(address _vaultProxy)
        external
        view
        returns (bool hasExecutableRequest_);

    function hasMigrationRequest(address _vaultProxy)
        external
        view
        returns (bool hasMigrationRequest_);

    function removeNominatedOwner() external;

    function setCurrentFundDeployer(address _nextFundDeployer) external;

    function setMigrationTimelock(uint256 _nextTimelock) external;

    function setNominatedOwner(address _nextNominatedOwner) external;

    function setSharesTokenSymbol(string calldata _nextSymbol) external;

    function signalMigration(
        address _vaultProxy,
        address _nextVaultAccessor,
        address _nextVaultLib,
        bool _bypassFailure
    ) external;
}

File 10 of 21 : IStakingWrapper.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.

    (c) Enzyme Council <[email protected]>

    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

/// @title IStakingWrapper interface
/// @author Enzyme Council <[email protected]>
interface IStakingWrapper {
    struct TotalHarvestData {
        uint128 integral;
        uint128 lastCheckpointBalance;
    }

    struct UserHarvestData {
        uint128 integral;
        uint128 claimableReward;
    }

    function claimRewardsFor(address _for)
        external
        returns (address[] memory rewardTokens_, uint256[] memory claimedAmounts_);

    function deposit(uint256 _amount) external;

    function depositTo(address _to, uint256 _amount) external;

    function withdraw(uint256 _amount, bool _claimRewards)
        external
        returns (address[] memory rewardTokens_, uint256[] memory claimedAmounts_);

    function withdrawTo(
        address _to,
        uint256 _amount,
        bool _claimRewardsToHolder
    ) external;

    function withdrawToOnBehalf(
        address _onBehalf,
        address _to,
        uint256 _amount,
        bool _claimRewardsToHolder
    ) external;

    // STATE GETTERS

    function getRewardTokenAtIndex(uint256 _index) external view returns (address rewardToken_);

    function getRewardTokenCount() external view returns (uint256 count_);

    function getRewardTokens() external view returns (address[] memory rewardTokens_);

    function getTotalHarvestDataForRewardToken(address _rewardToken)
        external
        view
        returns (TotalHarvestData memory totalHarvestData_);

    function getUserHarvestDataForRewardToken(address _user, address _rewardToken)
        external
        view
        returns (UserHarvestData memory userHarvestData_);

    function isPaused() external view returns (bool isPaused_);
}

File 11 of 21 : StakingWrapperBase.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.

    (c) Enzyme Council <[email protected]>

    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../../utils/AddressArrayLib.sol";
import "./IStakingWrapper.sol";

/// @title StakingWrapperBase Contract
/// @author Enzyme Council <[email protected]>
/// @notice A base contract for staking wrappers
/// @dev Can be used as a base for both standard deployments and proxy targets.
/// Draws on Convex's ConvexStakingWrapper implementation (https://github.com/convex-eth/platform/blob/main/contracts/contracts/wrappers/ConvexStakingWrapper.sol),
/// which is based on Curve.fi gauge wrappers (https://github.com/curvefi/curve-dao-contracts/tree/master/contracts/gauges/wrappers)
abstract contract StakingWrapperBase is IStakingWrapper, ERC20, ReentrancyGuard {
    using AddressArrayLib for address[];
    using SafeERC20 for ERC20;
    using SafeMath for uint256;

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

    event PauseToggled(bool isPaused);

    event RewardsClaimed(
        address caller,
        address indexed user,
        address[] rewardTokens,
        uint256[] claimedAmounts
    );

    event RewardTokenAdded(address token);

    event TotalHarvestIntegralUpdated(address indexed rewardToken, uint256 integral);

    event TotalHarvestLastCheckpointBalanceUpdated(
        address indexed rewardToken,
        uint256 lastCheckpointBalance
    );

    event UserHarvestUpdated(
        address indexed user,
        address indexed rewardToken,
        uint256 integral,
        uint256 claimableReward
    );

    event Withdrawn(
        address indexed caller,
        address indexed from,
        address indexed to,
        uint256 amount
    );

    uint256 private constant INTEGRAL_PRECISION = 1e18;
    address internal immutable OWNER;

    // Paused stops new deposits and checkpoints
    bool private paused;
    address[] private rewardTokens;
    mapping(address => TotalHarvestData) private rewardTokenToTotalHarvestData;
    mapping(address => mapping(address => UserHarvestData)) private rewardTokenToUserToHarvestData;

    modifier onlyOwner() {
        require(msg.sender == OWNER, "Only owner callable");
        _;
    }

    constructor(
        address _owner,
        string memory _tokenName,
        string memory _tokenSymbol
    ) public ERC20(_tokenName, _tokenSymbol) {
        OWNER = _owner;
    }

    /// @notice Toggles pause for deposit and harvesting new rewards
    /// @param _isPaused True if next state is paused, false if unpaused
    function togglePause(bool _isPaused) external onlyOwner {
        paused = _isPaused;

        emit PauseToggled(_isPaused);
    }

    ////////////////////////////
    // DEPOSITOR INTERACTIONS //
    ////////////////////////////

    // CLAIM REWARDS

    /// @notice Claims all rewards for a given account
    /// @param _for The account for which to claim rewards
    /// @return rewardTokens_ The reward tokens
    /// @return claimedAmounts_ The reward token amounts claimed
    /// @dev Can be called off-chain to simulate the total harvestable rewards for a particular user
    function claimRewardsFor(address _for)
        external
        override
        nonReentrant
        returns (address[] memory rewardTokens_, uint256[] memory claimedAmounts_)
    {
        return __checkpointAndClaim(_for);
    }

    // DEPOSIT

    /// @notice Deposits tokens to be staked, minting staking token to sender
    /// @param _amount The amount of tokens to deposit
    function deposit(uint256 _amount) external override {
        __deposit(msg.sender, msg.sender, _amount);
    }

    /// @notice Deposits tokens to be staked, minting staking token to a specified account
    /// @param _to The account to receive staking tokens
    /// @param _amount The amount of tokens to deposit
    function depositTo(address _to, uint256 _amount) external override {
        __deposit(msg.sender, _to, _amount);
    }

    /// @dev Helper to deposit tokens to be staked
    function __deposit(
        address _from,
        address _to,
        uint256 _amount
    ) private nonReentrant {
        require(!isPaused(), "__deposit: Paused");

        // Checkpoint before minting
        __checkpoint([_to, address(0)]);
        _mint(_to, _amount);

        __depositLogic(_from, _amount);

        emit Deposited(_from, _to, _amount);
    }

    // WITHDRAWAL

    /// @notice Withdraws staked tokens, returning tokens to the sender, and optionally claiming rewards
    /// @param _amount The amount of tokens to withdraw
    /// @param _claimRewards True if accrued rewards should be claimed
    /// @return rewardTokens_ The reward tokens
    /// @return claimedAmounts_ The reward token amounts claimed
    /// @dev Setting `_claimRewards` to true will save gas over separate calls to withdraw + claim
    function withdraw(uint256 _amount, bool _claimRewards)
        external
        override
        returns (address[] memory rewardTokens_, uint256[] memory claimedAmounts_)
    {
        return __withdraw(msg.sender, msg.sender, _amount, _claimRewards);
    }

    /// @notice Withdraws staked tokens, returning tokens to a specified account,
    /// and optionally claims rewards to the staked token holder
    /// @param _to The account to receive tokens
    /// @param _amount The amount of tokens to withdraw
    function withdrawTo(
        address _to,
        uint256 _amount,
        bool _claimRewardsToHolder
    ) external override {
        __withdraw(msg.sender, _to, _amount, _claimRewardsToHolder);
    }

    /// @notice Withdraws staked tokens on behalf of AccountA, returning tokens to a specified AccountB,
    /// and optionally claims rewards to the staked token holder
    /// @param _onBehalf The account on behalf to withdraw
    /// @param _to The account to receive tokens
    /// @param _amount The amount of tokens to withdraw
    /// @dev The caller must have an adequate ERC20.allowance() for _onBehalf
    function withdrawToOnBehalf(
        address _onBehalf,
        address _to,
        uint256 _amount,
        bool _claimRewardsToHolder
    ) external override {
        // Validate and reduce sender approval
        _approve(_onBehalf, msg.sender, allowance(_onBehalf, msg.sender).sub(_amount));

        __withdraw(_onBehalf, _to, _amount, _claimRewardsToHolder);
    }

    /// @dev Helper to withdraw staked tokens
    function __withdraw(
        address _from,
        address _to,
        uint256 _amount,
        bool _claimRewards
    )
        private
        nonReentrant
        returns (address[] memory rewardTokens_, uint256[] memory claimedAmounts_)
    {
        // Checkpoint before burning
        if (_claimRewards) {
            (rewardTokens_, claimedAmounts_) = __checkpointAndClaim(_from);
        } else {
            __checkpoint([_from, address(0)]);
        }

        _burn(_from, _amount);

        __withdrawLogic(_to, _amount);

        emit Withdrawn(msg.sender, _from, _to, _amount);

        return (rewardTokens_, claimedAmounts_);
    }

    /////////////
    // REWARDS //
    /////////////

    // Rewards tokens are added by the inheriting contract. Rewards tokens should be added, but not removed.
    // If new rewards tokens need to be added over time, that logic must be handled by the inheriting contract,
    // and can make use of __harvestRewardsLogic() if necessary

    // INTERNAL FUNCTIONS

    /// @dev Helper to add new reward tokens. Silently ignores duplicates.
    function __addRewardToken(address _rewardToken) internal {
        if (!rewardTokens.contains(_rewardToken)) {
            rewardTokens.push(_rewardToken);

            emit RewardTokenAdded(_rewardToken);
        }
    }

    // PRIVATE FUNCTIONS

    /// @dev Helper to calculate an unaccounted for reward amount due to a user based on integral values
    function __calcClaimableRewardForIntegralDiff(
        address _account,
        uint256 _totalHarvestIntegral,
        uint256 _userHarvestIntegral
    ) private view returns (uint256 claimableReward_) {
        return
            balanceOf(_account).mul(_totalHarvestIntegral.sub(_userHarvestIntegral)).div(
                INTEGRAL_PRECISION
            );
    }

    /// @dev Helper to calculate an unaccounted for integral amount based on checkpoint balance diff
    function __calcIntegralForBalDiff(
        uint256 _supply,
        uint256 _currentBalance,
        uint256 _lastCheckpointBalance
    ) private pure returns (uint256 integral_) {
        if (_supply > 0) {
            uint256 balDiff = _currentBalance.sub(_lastCheckpointBalance);
            if (balDiff > 0) {
                return balDiff.mul(INTEGRAL_PRECISION).div(_supply);
            }
        }

        return 0;
    }

    /// @dev Helper to checkpoint harvest data for specified accounts.
    /// Harvests all rewards prior to checkpoint.
    function __checkpoint(address[2] memory _accounts) private {
        // If paused, continue to checkpoint, but don't attempt to get new rewards
        if (!isPaused()) {
            __harvestRewardsLogic();
        }

        uint256 supply = totalSupply();

        uint256 rewardTokensLength = rewardTokens.length;
        for (uint256 i; i < rewardTokensLength; i++) {
            __updateHarvest(rewardTokens[i], _accounts, supply);
        }
    }

    /// @dev Helper to checkpoint harvest data for specified accounts.
    /// Harvests all rewards prior to checkpoint.
    function __checkpointAndClaim(address _account)
        private
        returns (address[] memory rewardTokens_, uint256[] memory claimedAmounts_)
    {
        // If paused, continue to checkpoint, but don't attempt to get new rewards
        if (!isPaused()) {
            __harvestRewardsLogic();
        }

        uint256 supply = totalSupply();

        rewardTokens_ = rewardTokens;
        claimedAmounts_ = new uint256[](rewardTokens_.length);
        for (uint256 i; i < rewardTokens_.length; i++) {
            claimedAmounts_[i] = __updateHarvestAndClaim(rewardTokens_[i], _account, supply);
        }

        emit RewardsClaimed(msg.sender, _account, rewardTokens_, claimedAmounts_);

        return (rewardTokens_, claimedAmounts_);
    }

    /// @dev Helper to update harvest data
    function __updateHarvest(
        address _rewardToken,
        address[2] memory _accounts,
        uint256 _supply
    ) private {
        TotalHarvestData storage totalHarvestData = rewardTokenToTotalHarvestData[_rewardToken];

        uint256 totalIntegral = totalHarvestData.integral;
        uint256 bal = ERC20(_rewardToken).balanceOf(address(this));
        uint256 integralToAdd = __calcIntegralForBalDiff(
            _supply,
            bal,
            totalHarvestData.lastCheckpointBalance
        );
        if (integralToAdd > 0) {
            totalIntegral = totalIntegral.add(integralToAdd);
            totalHarvestData.integral = uint128(totalIntegral);
            emit TotalHarvestIntegralUpdated(_rewardToken, totalIntegral);

            totalHarvestData.lastCheckpointBalance = uint128(bal);
            emit TotalHarvestLastCheckpointBalanceUpdated(_rewardToken, bal);
        }

        for (uint256 i; i < _accounts.length; i++) {
            // skip address(0), passed in upon mint and burn
            if (_accounts[i] == address(0)) continue;


                UserHarvestData storage userHarvestData
             = rewardTokenToUserToHarvestData[_rewardToken][_accounts[i]];

            uint256 userIntegral = userHarvestData.integral;
            if (userIntegral < totalIntegral) {
                uint256 claimableReward = uint256(userHarvestData.claimableReward).add(
                    __calcClaimableRewardForIntegralDiff(_accounts[i], totalIntegral, userIntegral)
                );

                userHarvestData.claimableReward = uint128(claimableReward);
                userHarvestData.integral = uint128(totalIntegral);

                emit UserHarvestUpdated(
                    _accounts[i],
                    _rewardToken,
                    totalIntegral,
                    claimableReward
                );
            }
        }
    }

    /// @dev Helper to update harvest data and claim all rewards to holder
    function __updateHarvestAndClaim(
        address _rewardToken,
        address _account,
        uint256 _supply
    ) private returns (uint256 claimedAmount_) {
        TotalHarvestData storage totalHarvestData = rewardTokenToTotalHarvestData[_rewardToken];

        uint256 totalIntegral = totalHarvestData.integral;
        uint256 integralToAdd = __calcIntegralForBalDiff(
            _supply,
            ERC20(_rewardToken).balanceOf(address(this)),
            totalHarvestData.lastCheckpointBalance
        );
        if (integralToAdd > 0) {
            totalIntegral = totalIntegral.add(integralToAdd);
            totalHarvestData.integral = uint128(totalIntegral);

            emit TotalHarvestIntegralUpdated(_rewardToken, totalIntegral);
        }


            UserHarvestData storage userHarvestData
         = rewardTokenToUserToHarvestData[_rewardToken][_account];

        uint256 userIntegral = userHarvestData.integral;
        claimedAmount_ = userHarvestData.claimableReward;
        if (userIntegral < totalIntegral) {
            userHarvestData.integral = uint128(totalIntegral);
            claimedAmount_ = claimedAmount_.add(
                __calcClaimableRewardForIntegralDiff(_account, totalIntegral, userIntegral)
            );

            emit UserHarvestUpdated(_account, _rewardToken, totalIntegral, claimedAmount_);
        }

        if (claimedAmount_ > 0) {
            userHarvestData.claimableReward = 0;
            ERC20(_rewardToken).safeTransfer(_account, claimedAmount_);

            emit UserHarvestUpdated(_account, _rewardToken, totalIntegral, 0);
        }

        // Repeat balance lookup since the reward token could have irregular transfer behavior
        uint256 finalBal = ERC20(_rewardToken).balanceOf(address(this));
        if (finalBal < totalHarvestData.lastCheckpointBalance) {
            totalHarvestData.lastCheckpointBalance = uint128(finalBal);

            emit TotalHarvestLastCheckpointBalanceUpdated(_rewardToken, finalBal);
        }

        return claimedAmount_;
    }

    ////////////////////////////////
    // REQUIRED VIRTUAL FUNCTIONS //
    ////////////////////////////////

    /// @dev Logic to be run during a deposit, specific to the integrated protocol.
    /// Do not mint staking tokens, which already happens during __deposit().
    function __depositLogic(address _onBehalf, uint256 _amount) internal virtual;

    /// @dev Logic to be run during a checkpoint to harvest new rewards, specific to the integrated protocol.
    /// Can also be used to add new rewards tokens dynamically.
    /// Do not checkpoint, only harvest the rewards.
    function __harvestRewardsLogic() internal virtual;

    /// @dev Logic to be run during a withdrawal, specific to the integrated protocol.
    /// Do not burn staking tokens, which already happens during __withdraw().
    function __withdrawLogic(address _to, uint256 _amount) internal virtual;

    /////////////////////
    // ERC20 OVERRIDES //
    /////////////////////

    /// @dev Overrides ERC20._transfer() in order to checkpoint sender and recipient pre-transfer rewards
    function _transfer(
        address _from,
        address _to,
        uint256 _amount
    ) internal override nonReentrant {
        __checkpoint([_from, _to]);
        super._transfer(_from, _to, _amount);
    }

    ///////////////////
    // STATE GETTERS //
    ///////////////////

    /// @notice Gets the reward token at a particular index
    /// @return rewardToken_ The reward token address
    function getRewardTokenAtIndex(uint256 _index)
        public
        view
        override
        returns (address rewardToken_)
    {
        return rewardTokens[_index];
    }

    /// @notice Gets the count of reward tokens being harvested
    /// @return count_ The count
    function getRewardTokenCount() public view override returns (uint256 count_) {
        return rewardTokens.length;
    }

    /// @notice Gets all reward tokens being harvested
    /// @return rewardTokens_ The reward tokens
    function getRewardTokens() public view override returns (address[] memory rewardTokens_) {
        return rewardTokens;
    }

    /// @notice Gets the TotalHarvestData for a specified reward token
    /// @param _rewardToken The reward token
    /// @return totalHarvestData_ The TotalHarvestData
    function getTotalHarvestDataForRewardToken(address _rewardToken)
        public
        view
        override
        returns (TotalHarvestData memory totalHarvestData_)
    {
        return rewardTokenToTotalHarvestData[_rewardToken];
    }

    /// @notice Gets the UserHarvestData for a specified account and reward token
    /// @param _user The account
    /// @param _rewardToken The reward token
    /// @return userHarvestData_ The UserHarvestData
    function getUserHarvestDataForRewardToken(address _user, address _rewardToken)
        public
        view
        override
        returns (UserHarvestData memory userHarvestData_)
    {
        return rewardTokenToUserToHarvestData[_rewardToken][_user];
    }

    /// @notice Checks if deposits and new reward harvesting are paused
    /// @return isPaused_ True if paused
    function isPaused() public view override returns (bool isPaused_) {
        return paused;
    }
}

File 12 of 21 : StakingWrapperLibBase.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.

    (c) Enzyme Council <[email protected]>

    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;

import "./StakingWrapperBase.sol";

/// @title StakingWrapperLibBase Contract
/// @author Enzyme Council <[email protected]>
/// @notice A staking wrapper base for proxy targets, extending StakingWrapperBase
abstract contract StakingWrapperLibBase is StakingWrapperBase {
    event TokenNameSet(string name);

    event TokenSymbolSet(string symbol);

    string private tokenName;
    string private tokenSymbol;

    /// @dev Helper function to set token name
    function __setTokenName(string memory _name) internal {
        tokenName = _name;

        emit TokenNameSet(_name);
    }

    /// @dev Helper function to set token symbol
    function __setTokenSymbol(string memory _symbol) internal {
        tokenSymbol = _symbol;

        emit TokenSymbolSet(_symbol);
    }

    /////////////////////
    // ERC20 OVERRIDES //
    /////////////////////

    /// @notice Gets the token name
    /// @return name_ The token name
    /// @dev Overrides the constructor-set storage for use in proxies
    function name() public view override returns (string memory name_) {
        return tokenName;
    }

    /// @notice Gets the token symbol
    /// @return symbol_ The token symbol
    /// @dev Overrides the constructor-set storage for use in proxies
    function symbol() public view override returns (string memory symbol_) {
        return tokenSymbol;
    }
}

File 13 of 21 : ConvexCurveLpStakingWrapperLib.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.

    (c) Enzyme Council <[email protected]>

    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "../../../interfaces/IConvexBaseRewardPool.sol";
import "../../../interfaces/IConvexBooster.sol";
import "../../../interfaces/IConvexVirtualBalanceRewardPool.sol";
import "../StakingWrapperLibBase.sol";

/// @title ConvexCurveLpStakingWrapperLib Contract
/// @author Enzyme Council <[email protected]>
/// @notice A library contract for ConvexCurveLpStakingWrapper instances
contract ConvexCurveLpStakingWrapperLib is StakingWrapperLibBase {
    IConvexBooster private immutable CONVEX_BOOSTER_CONTRACT;
    address private immutable CRV_TOKEN;
    address private immutable CVX_TOKEN;

    address private convexPool;
    uint256 private convexPoolId;
    address private curveLPToken;

    constructor(
        address _owner,
        address _convexBooster,
        address _crvToken,
        address _cvxToken
    ) public StakingWrapperBase(_owner, "", "") {
        CONVEX_BOOSTER_CONTRACT = IConvexBooster(_convexBooster);
        CRV_TOKEN = _crvToken;
        CVX_TOKEN = _cvxToken;
    }

    /// @notice Initializes the proxy
    /// @param _pid The Convex pool id for which to use the proxy
    function init(uint256 _pid) external {
        // Can validate with any variable set here
        require(getCurveLpToken() == address(0), "init: Initialized");

        IConvexBooster.PoolInfo memory poolInfo = CONVEX_BOOSTER_CONTRACT.poolInfo(_pid);

        // Set ERC20 info on proxy
        __setTokenName(string(abi.encodePacked("Enzyme Staked: ", ERC20(poolInfo.token).name())));
        __setTokenSymbol(string(abi.encodePacked("stk", ERC20(poolInfo.token).symbol())));

        curveLPToken = poolInfo.lptoken;
        convexPool = poolInfo.crvRewards;
        convexPoolId = _pid;

        __addRewardToken(CRV_TOKEN);
        __addRewardToken(CVX_TOKEN);
        addExtraRewards();

        setApprovals();
    }

    /// @notice Adds rewards tokens that have not yet been added to the wrapper
    /// @dev Anybody can call, in case more pool tokens are added.
    /// Is called prior to every new harvest.
    function addExtraRewards() public {
        IConvexBaseRewardPool convexPoolContract = IConvexBaseRewardPool(getConvexPool());
        // Could probably exit early after validating that extraRewardsCount + 2 <= rewardsTokens.length,
        // but this protects against a reward token being removed that still needs to be paid out
        uint256 extraRewardsCount = convexPoolContract.extraRewardsLength();
        for (uint256 i; i < extraRewardsCount; i++) {
            // __addRewardToken silently ignores duplicates
            __addRewardToken(
                IConvexVirtualBalanceRewardPool(convexPoolContract.extraRewards(i)).rewardToken()
            );
        }
    }

    /// @notice Sets necessary ERC20 approvals, as-needed
    function setApprovals() public {
        ERC20(getCurveLpToken()).safeApprove(address(CONVEX_BOOSTER_CONTRACT), type(uint256).max);
    }

    ////////////////////////////////
    // STAKING WRAPPER BASE LOGIC //
    ////////////////////////////////

    /// @dev Logic to be run during a deposit, specific to the integrated protocol.
    /// Do not mint staking tokens, which already happens during __deposit().
    function __depositLogic(address _from, uint256 _amount) internal override {
        ERC20(getCurveLpToken()).safeTransferFrom(_from, address(this), _amount);
        CONVEX_BOOSTER_CONTRACT.deposit(convexPoolId, _amount, true);
    }

    /// @dev Logic to be run during a checkpoint to harvest new rewards, specific to the integrated protocol.
    /// Can also be used to add new rewards tokens dynamically.
    /// Do not checkpoint, only harvest the rewards.
    function __harvestRewardsLogic() internal override {
        // It's probably overly-cautious to check rewards on every call,
        // but even when the pool has 1 extra reward token (most have 0) it only adds ~10-15k gas units,
        // so more convenient to always check than to monitor for rewards changes.
        addExtraRewards();
        IConvexBaseRewardPool(getConvexPool()).getReward();
    }

    /// @dev Logic to be run during a withdrawal, specific to the integrated protocol.
    /// Do not burn staking tokens, which already happens during __withdraw().
    function __withdrawLogic(address _to, uint256 _amount) internal override {
        IConvexBaseRewardPool(getConvexPool()).withdrawAndUnwrap(_amount, false);
        ERC20(getCurveLpToken()).safeTransfer(_to, _amount);
    }

    ///////////////////
    // STATE GETTERS //
    ///////////////////

    /// @notice Gets the associated Convex reward pool address
    /// @return convexPool_ The reward pool
    function getConvexPool() public view returns (address convexPool_) {
        return convexPool;
    }

    /// @notice Gets the associated Convex reward pool id (pid)
    /// @return convexPoolId_ The pid
    function getConvexPoolId() public view returns (uint256 convexPoolId_) {
        return convexPoolId;
    }

    /// @notice Gets the associated Curve LP token
    /// @return curveLPToken_ The Curve LP token
    function getCurveLpToken() public view returns (address curveLPToken_) {
        return curveLPToken;
    }
}

File 14 of 21 : IConvexBaseRewardPool.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.
    (c) Enzyme Council <[email protected]>
    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;

/// @title IConvexBaseRewardPool Interface
/// @author Enzyme Council <[email protected]>
interface IConvexBaseRewardPool {
    function extraRewards(uint256) external view returns (address);

    function extraRewardsLength() external view returns (uint256);

    function getReward() external returns (bool);

    function withdrawAndUnwrap(uint256, bool) external returns (bool);
}

File 15 of 21 : IConvexBooster.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.
    (c) Enzyme Council <[email protected]>
    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

/// @title IConvexBooster Interface
/// @author Enzyme Council <[email protected]>
interface IConvexBooster {
    struct PoolInfo {
        address lptoken;
        address token;
        address gauge;
        address crvRewards;
        address stash;
        bool shutdown;
    }

    function deposit(
        uint256,
        uint256,
        bool
    ) external returns (bool);

    function poolInfo(uint256) external view returns (PoolInfo memory);
}

File 16 of 21 : IConvexVirtualBalanceRewardPool.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.
    (c) Enzyme Council <[email protected]>
    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;

/// @title IConvexVirtualBalanceRewardPool Interface
/// @author Enzyme Council <[email protected]>
interface IConvexVirtualBalanceRewardPool {
    function rewardToken() external view returns (address);
}

File 17 of 21 : AddressArrayLib.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.

    (c) Enzyme Council <[email protected]>

    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;

/// @title AddressArray Library
/// @author Enzyme Council <[email protected]>
/// @notice A library to extend the address array data type
library AddressArrayLib {
    /////////////
    // STORAGE //
    /////////////

    /// @dev Helper to remove an item from a storage array
    function removeStorageItem(address[] storage _self, address _itemToRemove)
        internal
        returns (bool removed_)
    {
        uint256 itemCount = _self.length;
        for (uint256 i; i < itemCount; i++) {
            if (_self[i] == _itemToRemove) {
                if (i < itemCount - 1) {
                    _self[i] = _self[itemCount - 1];
                }
                _self.pop();
                removed_ = true;
                break;
            }
        }

        return removed_;
    }

    ////////////
    // MEMORY //
    ////////////

    /// @dev Helper to add an item to an array. Does not assert uniqueness of the new item.
    function addItem(address[] memory _self, address _itemToAdd)
        internal
        pure
        returns (address[] memory nextArray_)
    {
        nextArray_ = new address[](_self.length + 1);
        for (uint256 i; i < _self.length; i++) {
            nextArray_[i] = _self[i];
        }
        nextArray_[_self.length] = _itemToAdd;

        return nextArray_;
    }

    /// @dev Helper to add an item to an array, only if it is not already in the array.
    function addUniqueItem(address[] memory _self, address _itemToAdd)
        internal
        pure
        returns (address[] memory nextArray_)
    {
        if (contains(_self, _itemToAdd)) {
            return _self;
        }

        return addItem(_self, _itemToAdd);
    }

    /// @dev Helper to verify if an array contains a particular value
    function contains(address[] memory _self, address _target)
        internal
        pure
        returns (bool doesContain_)
    {
        for (uint256 i; i < _self.length; i++) {
            if (_target == _self[i]) {
                return true;
            }
        }
        return false;
    }

    /// @dev Helper to merge the unique items of a second array.
    /// Does not consider uniqueness of either array, only relative uniqueness.
    /// Preserves ordering.
    function mergeArray(address[] memory _self, address[] memory _arrayToMerge)
        internal
        pure
        returns (address[] memory nextArray_)
    {
        uint256 newUniqueItemCount;
        for (uint256 i; i < _arrayToMerge.length; i++) {
            if (!contains(_self, _arrayToMerge[i])) {
                newUniqueItemCount++;
            }
        }

        if (newUniqueItemCount == 0) {
            return _self;
        }

        nextArray_ = new address[](_self.length + newUniqueItemCount);
        for (uint256 i; i < _self.length; i++) {
            nextArray_[i] = _self[i];
        }
        uint256 nextArrayIndex = _self.length;
        for (uint256 i; i < _arrayToMerge.length; i++) {
            if (!contains(_self, _arrayToMerge[i])) {
                nextArray_[nextArrayIndex] = _arrayToMerge[i];
                nextArrayIndex++;
            }
        }

        return nextArray_;
    }

    /// @dev Helper to verify if array is a set of unique values.
    /// Does not assert length > 0.
    function isUniqueSet(address[] memory _self) internal pure returns (bool isUnique_) {
        if (_self.length <= 1) {
            return true;
        }

        uint256 arrayLength = _self.length;
        for (uint256 i; i < arrayLength; i++) {
            for (uint256 j = i + 1; j < arrayLength; j++) {
                if (_self[i] == _self[j]) {
                    return false;
                }
            }
        }

        return true;
    }

    /// @dev Helper to remove items from an array. Removes all matching occurrences of each item.
    /// Does not assert uniqueness of either array.
    function removeItems(address[] memory _self, address[] memory _itemsToRemove)
        internal
        pure
        returns (address[] memory nextArray_)
    {
        if (_itemsToRemove.length == 0) {
            return _self;
        }

        bool[] memory indexesToRemove = new bool[](_self.length);
        uint256 remainingItemsCount = _self.length;
        for (uint256 i; i < _self.length; i++) {
            if (contains(_itemsToRemove, _self[i])) {
                indexesToRemove[i] = true;
                remainingItemsCount--;
            }
        }

        if (remainingItemsCount == _self.length) {
            nextArray_ = _self;
        } else if (remainingItemsCount > 0) {
            nextArray_ = new address[](remainingItemsCount);
            uint256 nextArrayIndex;
            for (uint256 i; i < _self.length; i++) {
                if (!indexesToRemove[i]) {
                    nextArray_[nextArrayIndex] = _self[i];
                    nextArrayIndex++;
                }
            }
        }

        return nextArray_;
    }
}

File 18 of 21 : BeaconProxy.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.

    (c) Enzyme Council <[email protected]>

    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;

import "./IBeacon.sol";

/// @title BeaconProxy Contract
/// @author Enzyme Council <[email protected]>
/// @notice A proxy contract that uses the beacon pattern for instant upgrades
contract BeaconProxy {
    address private immutable BEACON;

    constructor(bytes memory _constructData, address _beacon) public {
        BEACON = _beacon;

        (bool success, bytes memory returnData) = IBeacon(_beacon).getCanonicalLib().delegatecall(
            _constructData
        );
        require(success, string(returnData));
    }

    // solhint-disable-next-line no-complex-fallback
    fallback() external payable {
        address contractLogic = IBeacon(BEACON).getCanonicalLib();
        assembly {
            calldatacopy(0x0, 0x0, calldatasize())
            let success := delegatecall(
                sub(gas(), 10000),
                contractLogic,
                0x0,
                calldatasize(),
                0,
                0
            )
            let retSz := returndatasize()
            returndatacopy(0, 0, retSz)
            switch success
                case 0 {
                    revert(0, retSz)
                }
                default {
                    return(0, retSz)
                }
        }
    }

    receive() external payable {}
}

File 19 of 21 : BeaconProxyFactory.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.

    (c) Enzyme Council <[email protected]>

    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;

import "./BeaconProxy.sol";
import "./IBeaconProxyFactory.sol";

/// @title BeaconProxyFactory Contract
/// @author Enzyme Council <[email protected]>
/// @notice Factory contract that deploys beacon proxies
abstract contract BeaconProxyFactory is IBeaconProxyFactory {
    event CanonicalLibSet(address nextCanonicalLib);

    event ProxyDeployed(address indexed caller, address proxy, bytes constructData);

    address private canonicalLib;

    constructor(address _canonicalLib) public {
        __setCanonicalLib(_canonicalLib);
    }

    /// @notice Deploys a new proxy instance
    /// @param _constructData The constructor data with which to call `init()` on the deployed proxy
    /// @return proxy_ The proxy address
    function deployProxy(bytes memory _constructData) public override returns (address proxy_) {
        proxy_ = address(new BeaconProxy(_constructData, address(this)));

        emit ProxyDeployed(msg.sender, proxy_, _constructData);

        return proxy_;
    }

    /// @notice Gets the canonical lib used by all proxies
    /// @return canonicalLib_ The canonical lib
    function getCanonicalLib() public view override returns (address canonicalLib_) {
        return canonicalLib;
    }

    /// @notice Gets the contract owner
    /// @return owner_ The contract owner
    function getOwner() public view virtual returns (address owner_);

    /// @notice Sets the next canonical lib used by all proxies
    /// @param _nextCanonicalLib The next canonical lib
    function setCanonicalLib(address _nextCanonicalLib) public override {
        require(
            msg.sender == getOwner(),
            "setCanonicalLib: Only the owner can call this function"
        );

        __setCanonicalLib(_nextCanonicalLib);
    }

    /// @dev Helper to set the next canonical lib
    function __setCanonicalLib(address _nextCanonicalLib) internal {
        canonicalLib = _nextCanonicalLib;

        emit CanonicalLibSet(_nextCanonicalLib);
    }
}

File 20 of 21 : IBeacon.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.

    (c) Enzyme Council <[email protected]>

    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

pragma solidity 0.6.12;

/// @title IBeacon interface
/// @author Enzyme Council <[email protected]>
interface IBeacon {
    function getCanonicalLib() external view returns (address);
}

File 21 of 21 : IBeaconProxyFactory.sol
// SPDX-License-Identifier: GPL-3.0

/*
    This file is part of the Enzyme Protocol.

    (c) Enzyme Council <[email protected]>

    For the full license information, please view the LICENSE
    file that was distributed with this source code.
*/

import "./IBeacon.sol";

pragma solidity 0.6.12;

/// @title IBeaconProxyFactory interface
/// @author Enzyme Council <[email protected]>
interface IBeaconProxyFactory is IBeacon {
    function deployProxy(bytes memory _constructData) external returns (address proxy_);

    function setCanonicalLib(address _canonicalLib) external;
}

Settings
{
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "details": {
      "constantOptimizer": true,
      "cse": true,
      "deduplicate": true,
      "jumpdestRemover": true,
      "orderLiterals": true,
      "peephole": true,
      "yul": false
    },
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_dispatcher","type":"address"},{"internalType":"address","name":"_convexBooster","type":"address"},{"internalType":"address","name":"_crvToken","type":"address"},{"internalType":"address","name":"_cvxToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"nextCanonicalLib","type":"address"}],"name":"CanonicalLibSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"proxy","type":"address"},{"indexed":false,"internalType":"bytes","name":"constructData","type":"bytes"}],"name":"ProxyDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"address","name":"wrapperProxy","type":"address"},{"indexed":false,"internalType":"address","name":"curveLpToken","type":"address"}],"name":"WrapperDeployed","type":"event"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"deploy","outputs":[{"internalType":"address","name":"wrapperProxy_","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_constructData","type":"bytes"}],"name":"deployProxy","outputs":[{"internalType":"address","name":"proxy_","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCanonicalLib","outputs":[{"internalType":"address","name":"canonicalLib_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wrapper","type":"address"}],"name":"getCurveLpTokenForWrapper","outputs":[{"internalType":"address","name":"lpToken_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOwner","outputs":[{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"getWrapperForConvexPool","outputs":[{"internalType":"address","name":"wrapper_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_wrappers","type":"address[]"}],"name":"pauseWrappers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_nextCanonicalLib","type":"address"}],"name":"setCanonicalLib","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_wrappers","type":"address[]"}],"name":"unpauseWrappers","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b50604051620047653803806200476583398101604081905262000034916200012b565b60006200004181620000b2565b50836001600160a01b03166080816001600160a01b031660601b81525050620000a83084848460405162000075906200010a565b620000849493929190620001b6565b604051809103906000f080158015620000a1573d6000803e3d6000fd5b50620000b2565b5050505062000228565b600080546001600160a01b0319166001600160a01b0383161790556040517f9007c6c0b3b1622c381c19563620e3ce8b824e958af3020495bd6654e8a0310a90620000ff908390620001a6565b60405180910390a150565b61357f80620011e683390190565b805162000125816200020e565b92915050565b600080600080608085870312156200014257600080fd5b600062000150878762000118565b9450506020620001638782880162000118565b9350506040620001768782880162000118565b9250506060620001898782880162000118565b91505092959194509250565b620001a081620001fc565b82525050565b6020810162000125828462000195565b60808101620001c6828762000195565b620001d5602083018662000195565b620001e4604083018562000195565b620001f3606083018462000195565b95945050505050565b60006001600160a01b03821662000125565b6200021981620001fc565b81146200022557600080fd5b50565b60805160601c610fa0620002466000398061023a5250610fa06000f3fe60806040523480156200001157600080fd5b5060043610620000a05760003560e01c80638aeb8b84116200006f5780638aeb8b84146200010c57806397b02f0c146200012557806398a7c4c7146200013c578063a5e387511462000146578063d0ac1a8d146200015d57620000a0565b80630c0872f514620000a55780631c25201c14620000d4578063456b4a3714620000eb578063893d20e81462000102575b600080fd5b620000bc620000b636600462000866565b62000174565b604051620000cb919062000a05565b60405180910390f35b620000bc620000e53660046200089f565b620001fd565b620000bc620000fc366004620007d6565b62000218565b620000bc62000236565b620001236200011d36600462000820565b620002d2565b005b620001236200013636600462000820565b620003bc565b620000bc62000498565b620000bc620001573660046200089f565b620004a7565b620001236200016e366004620007d6565b62000650565b600081306040516200018690620006f3565b6200019392919062000a6f565b604051809103906000f080158015620001b0573d6000803e3d6000fd5b509050336001600160a01b03167f8f1eab90fee81d885732c2e07a3a9b9f6c32278756471123fa57486858c3f7018284604051620001f092919062000a3b565b60405180910390a2919050565b6000908152600160205260409020546001600160a01b031690565b6001600160a01b039081166000908152600260205260409020541690565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663893d20e86040518163ffffffff1660e01b815260040160206040518083038186803b1580156200029257600080fd5b505afa158015620002a7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002cd9190620007ff565b905090565b620002dc62000236565b6001600160a01b0316336001600160a01b031614620003185760405162461bcd60e51b81526004016200030f9062000a93565b60405180910390fd5b60005b81811015620003b7578282828181106200033157fe5b9050602002016020810190620003489190620007d6565b6001600160a01b03166357d159c660006040518263ffffffff1660e01b815260040162000376919062000a5f565b600060405180830381600087803b1580156200039157600080fd5b505af1158015620003a6573d6000803e3d6000fd5b5050600190920191506200031b9050565b505050565b620003c662000236565b6001600160a01b0316336001600160a01b031614620003f95760405162461bcd60e51b81526004016200030f9062000a93565b60005b81811015620003b7578282828181106200041257fe5b9050602002016020810190620004299190620007d6565b6001600160a01b03166357d159c660016040518263ffffffff1660e01b815260040162000457919062000a5f565b600060405180830381600087803b1580156200047257600080fd5b505af115801562000487573d6000803e3d6000fd5b505060019092019150620003fc9050565b6000546001600160a01b031690565b600080620004b583620001fd565b6001600160a01b031614620004de5760405162461bcd60e51b81526004016200030f9062000aa5565b606063b7b0422d60e01b83604051602401620004fb919062000ac9565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290506200053a8162000174565b600084815260016020908152604080832080546001600160a01b0319166001600160a01b0386169081179091558151632e266ba960e21b81529151949650929363b899aea492600480840193919291829003018186803b1580156200059e57600080fd5b505afa158015620005b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005d99190620007ff565b6001600160a01b038481166000908152600260205260409081902080546001600160a01b031916928416929092179091555190915084907fe547dfbae0eb3c2c335669b841fa076eb2a1a64eab9bd2fb49868b5e624586f19062000641908690859062000a15565b60405180910390a25050919050565b6200065a62000236565b6001600160a01b0316336001600160a01b0316146200068d5760405162461bcd60e51b81526004016200030f9062000ab7565b62000698816200069b565b50565b600080546001600160a01b0319166001600160a01b0383161790556040517f9007c6c0b3b1622c381c19563620e3ce8b824e958af3020495bd6654e8a0310a90620006e890839062000a05565b60405180910390a150565b6103a78062000bc483390190565b80356200070e8162000ba1565b92915050565b80516200070e8162000ba1565b60008083601f8401126200073457600080fd5b50813567ffffffffffffffff8111156200074d57600080fd5b6020830191508360208202830111156200076657600080fd5b9250929050565b600082601f8301126200077f57600080fd5b813562000796620007908262000b01565b62000ad9565b91508082526020830160208301858383011115620007b357600080fd5b620007c083828462000b58565b50505092915050565b80356200070e8162000bb8565b600060208284031215620007e957600080fd5b6000620007f7848462000701565b949350505050565b6000602082840312156200081257600080fd5b6000620007f7848462000714565b600080602083850312156200083457600080fd5b823567ffffffffffffffff8111156200084c57600080fd5b6200085a8582860162000721565b92509250509250929050565b6000602082840312156200087957600080fd5b813567ffffffffffffffff8111156200089157600080fd5b620007f7848285016200076d565b600060208284031215620008b257600080fd5b6000620007f78484620007c9565b620008cb8162000b37565b82525050565b620008cb8162000b44565b6000620008e98262000b2a565b620008f5818562000b2e565b93506200090781856020860162000b64565b620009128162000b97565b9093019392505050565b60006200092b60258362000b2e565b7f4f6e6c7920746865206f776e65722063616e2063616c6c20746869732066756e81526431ba34b7b760d91b602082015260400192915050565b600062000974601e8362000b2e565b7f6465706c6f793a205772617070657220616c7265616479206578697374730000815260200192915050565b6000620009af60368362000b2e565b7f73657443616e6f6e6963616c4c69623a204f6e6c7920746865206f776e65722081527531b0b71031b0b636103a3434b990333ab731ba34b7b760511b602082015260400192915050565b620008cb8162000b55565b602081016200070e8284620008c0565b6040810162000a258285620008c0565b62000a346020830184620008c0565b9392505050565b6040810162000a4b8285620008c0565b8181036020830152620007f78184620008dc565b602081016200070e8284620008d1565b6040808252810162000a828185620008dc565b905062000a346020830184620008c0565b602080825281016200070e816200091c565b602080825281016200070e8162000965565b602080825281016200070e81620009a0565b602081016200070e8284620009fa565b60405181810167ffffffffffffffff8111828210171562000af957600080fd5b604052919050565b600067ffffffffffffffff82111562000b1957600080fd5b506020601f91909101601f19160190565b5190565b90815260200190565b60006200070e8262000b49565b151590565b6001600160a01b031690565b90565b82818337506000910152565b60005b8381101562000b8157818101518382015260200162000b67565b8381111562000b91576000848401525b50505050565b601f01601f191690565b62000bac8162000b37565b81146200069857600080fd5b62000bac8162000b5556fe60a060405234801561001057600080fd5b506040516103a73803806103a78339818101604052604081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040818152602092830151606081811b6001600160601b0319166080526398a7c4c760e01b845291519095506000945090926001600160a01b038616926398a7c4c79260048083019392829003018186803b15801561013b57600080fd5b505afa15801561014f573d6000803e3d6000fd5b505050506040513d602081101561016557600080fd5b505160405185516001600160a01b0390921691869190819060208401908083835b602083106101a55780518252601f199092019160209182019101610186565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855af49150503d8060008114610205576040519150601f19603f3d011682016040523d82523d6000602084013e61020a565b606091505b509150915081819061029a5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561025f578181015183820152602001610247565b50505050905090810190601f16801561028c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050505060805160601c60ee6102b960003980600e525060ee6000f3fe608060405236600a57005b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166398a7c4c76040518163ffffffff1660e01b815260040160206040518083038186803b158015606457600080fd5b505afa1580156077573d6000803e3d6000fd5b505050506040513d6020811015608c57600080fd5b505190503660008037600080366000846127105a03f43d806000803e81801560b357816000f35b816000fdfea26469706673582212209a2062a062ba495cf18ec0c812d8c737e96be9cba7dae39d1b5ab98efe85b85764736f6c634300060c0033a264697066735822122032d315117304e289ab7dc4c6ed7d722c289d06698e282c86a8c6bcae2d42e7e164736f6c634300060c00336101006040523480156200001257600080fd5b506040516200357f3803806200357f833981016040819052620000359162000174565b604080516020808201808452600080845284519283019094529281528151879383918391620000689160039190620000c5565b5080516200007e906004906020840190620000c5565b50506005805460ff1916601217905550506001600655506001600160601b0319606091821b811660805293811b841660a05291821b831660c052901b1660e052506200020a565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200010857805160ff191683800117855562000138565b8280016001018555821562000138579182015b82811115620001385782518255916020019190600101906200011b565b50620001469291506200014a565b5090565b5b808211156200014657600081556001016200014b565b80516200016e81620001f0565b92915050565b600080600080608085870312156200018b57600080fd5b600062000199878762000161565b9450506020620001ac8782880162000161565b9350506040620001bf8782880162000161565b9250506060620001d28782880162000161565b91505092959194509250565b60006001600160a01b0382166200016e565b620001fb81620001de565b81146200020757600080fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6133296200025660003980610d16525080610ced5250806108f65280610ae95280611dd152508061083752506133296000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c806382e5d07311610104578063b187bd26116100a2578063c4f59f9b11610071578063c4f59f9b146103c6578063d12f8df0146103db578063dd62ed3e146103e3578063ffaad6a5146103f6576101da565b8063b187bd2614610390578063b6b55f2514610398578063b7b0422d146103ab578063b899aea4146103be576101da565b80639655dd61116100de5780639655dd6114610344578063a457c2d714610357578063a7d2793f1461036a578063a9059cbb1461037d576101da565b806382e5d0731461032c5780638757b15b1461033457806395d89b411461033c576101da565b806323b872dd1161017c578063395093511161014b57806339509351146102e057806357d159c6146102f357806370a082311461030657806373e2290c14610319576101da565b806323b872dd146102925780632cfafabe146102a5578063313ce567146102b857806338d07436146102cd576101da565b8063095ea7b3116101b8578063095ea7b31461022757806318160ddd146102475780631ac6d19d1461025c57806320c718101461027d576101da565b80630663b22c146101df57806306fdde03146101e9578063093f636514610207575b600080fd5b6101e7610409565b005b6101f1610597565b6040516101fe9190612f82565b60405180910390f35b61021a6102153660046125cd565b61062d565b6040516101fe91906130b3565b61023a6102353660046126b5565b610688565b6040516101fe9190612f74565b61024f6106a5565b6040516101fe91906130c1565b61026f61026a366004612591565b6106ab565b6040516101fe929190612f4f565b6102856106f7565b6040516101fe9190612e95565b61023a6102a0366004612607565b610706565b6101e76102b3366004612654565b61078e565b6102c06107bb565b6040516101fe919061313b565b61026f6102db3660046127f3565b6107c4565b61023a6102ee3660046126b5565b6107de565b6101e7610301366004612728565b61082c565b61024f610314366004612591565b6108bd565b6101e76103273660046126e5565b6108d8565b61024f6108eb565b6101e76108f1565b6101f1610932565b61021a610352366004612591565b610993565b61023a6103653660046126b5565b6109dd565b6102856103783660046127b7565b610a45565b61023a61038b3660046126b5565b610a6f565b61023a610a83565b6101e76103a63660046127b7565b610a8c565b6101e76103b93660046127b7565b610a9a565b610285610d4e565b6103ce610d5d565b6040516101fe9190612f3e565b61024f610dbe565b61024f6103f13660046125cd565b610dc4565b6101e76104043660046126b5565b610def565b60006104136106f7565b90506000816001600160a01b031663d55a23f46040518163ffffffff1660e01b815260040160206040518083038186803b15801561045057600080fd5b505afa158015610464573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061048891906127d5565b905060005b8181101561059257604051632061aa2360e11b815261058a906001600160a01b038516906340c35446906104c59085906004016130c1565b60206040518083038186803b1580156104dd57600080fd5b505afa1580156104f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061051591906125af565b6001600160a01b031663f7c618c16040518163ffffffff1660e01b815260040160206040518083038186803b15801561054d57600080fd5b505afa158015610561573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061058591906125af565b610dfa565b60010161048d565b505050565b600b8054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106235780601f106105f857610100808354040283529160200191610623565b820191906000526020600020905b81548152906001019060200180831161060657829003601f168201915b5050505050905090565b610635612385565b506001600160a01b038082166000908152600a602090815260408083209386168352928152908290208251808401909352546001600160801b038082168452600160801b90910416908201525b92915050565b600061069c610695610ee6565b8484610eea565b50600192915050565b60025490565b606080600260065414156106da5760405162461bcd60e51b81526004016106d190613083565b60405180910390fd5b60026006556106e883610f9e565b60016006559094909350915050565b600d546001600160a01b031690565b60006107138484846110f3565b6107838461071f610ee6565b61077e856040518060600160405280602881526020016132a7602891396001600160a01b038a1660009081526001602052604081209061075d610ee6565b6001600160a01b031681526020810191909152604001600020549190611157565b610eea565b5060015b9392505050565b6107a7843361077e856107a18933610dc4565b90611183565b6107b3848484846111ab565b505050505050565b60055460ff1690565b6060806107d3333386866111ab565b915091509250929050565b600061069c6107eb610ee6565b8461077e85600160006107fc610ee6565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549061128e565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146108745760405162461bcd60e51b81526004016106d190612fa3565b6007805460ff19168215151790556040517f9077d36bc00859b5c3f320310707208543dd35092cb0a0fe117d0c6a558b148b906108b2908390612f74565b60405180910390a150565b6001600160a01b031660009081526020819052604090205490565b6108e4338484846111ab565b5050505050565b60085490565b6109307f0000000000000000000000000000000000000000000000000000000000000000600019610920610d4e565b6001600160a01b031691906112b3565b565b600c8054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106235780601f106105f857610100808354040283529160200191610623565b61099b612385565b506001600160a01b03166000908152600960209081526040918290208251808401909352546001600160801b038082168452600160801b909104169082015290565b600061069c6109ea610ee6565b8461077e856040518060600160405280602581526020016132cf6025913960016000610a14610ee6565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190611157565b600060088281548110610a5457fe5b6000918252602090912001546001600160a01b031692915050565b600061069c610a7c610ee6565b84846110f3565b60075460ff1690565b610a973333836113ad565b50565b6000610aa4610d4e565b6001600160a01b031614610aca5760405162461bcd60e51b81526004016106d190613023565b610ad261239c565b604051631526fe2760e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631526fe2790610b1e9085906004016130c1565b60c06040518083038186803b158015610b3657600080fd5b505afa158015610b4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6e9190612799565b9050610c1081602001516001600160a01b03166306fdde036040518163ffffffff1660e01b815260040160006040518083038186803b158015610bb057600080fd5b505afa158015610bc4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610bec9190810190612764565b604051602001610bfc9190612e73565b604051602081830303815290604052611489565b610cb081602001516001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b158015610c5057600080fd5b505afa158015610c64573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c8c9190810190612764565b604051602001610c9c9190612e8a565b6040516020818303038152906040526114cc565b8051600f80546001600160a01b039283166001600160a01b0319918216179091556060830151600d8054919093169116179055600e829055610d117f0000000000000000000000000000000000000000000000000000000000000000610dfa565b610d3a7f0000000000000000000000000000000000000000000000000000000000000000610dfa565b610d42610409565b610d4a6108f1565b5050565b600f546001600160a01b031690565b6060600880548060200260200160405190810160405280929190818152602001828054801561062357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610d97575050505050905090565b600e5490565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b610d4a3383836113ad565b610e67816008805480602002602001604051908101604052809291908181526020018280548015610e5457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610e36575b505050505061150f90919063ffffffff16565b610a9757600880546001810182556000919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30180546001600160a01b0319166001600160a01b0383161790556040517ff3e4c2c64e71e6ba2eaab9a599bced62f9eb91d2cda610bf41aa8c80ff2cf826906108b2908390612e95565b3390565b6001600160a01b038316610f105760405162461bcd60e51b81526004016106d190613053565b6001600160a01b038216610f365760405162461bcd60e51b81526004016106d190612fc3565b6001600160a01b0380841660008181526001602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610f919085906130c1565b60405180910390a3505050565b606080610fa9610a83565b610fb557610fb5611565565b6000610fbf6106a5565b600880546040805160208084028201810190925282815293945083018282801561101257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ff4575b50505050509250825167ffffffffffffffff8111801561103157600080fd5b5060405190808252806020026020018201604052801561105b578160200160208202803683370190505b50915060005b83518110156110a75761108884828151811061107957fe5b602002602001015186846115e7565b83828151811061109457fe5b6020908102919091010152600101611061565b50836001600160a01b03167fc17f1458d7773c19369fc6c68a3b4c44b675c86c50c997d58853aed5e38de6cd3385856040516110e593929190612ea3565b60405180910390a250915091565b600260065414156111165760405162461bcd60e51b81526004016106d190613083565b6002600655604080518082019091526001600160a01b038085168252831660208201526111429061194f565b61114d8383836119b8565b5050600160065550565b6000818484111561117b5760405162461bcd60e51b81526004016106d19190612f82565b505050900390565b6000828211156111a55760405162461bcd60e51b81526004016106d190612fe3565b50900390565b606080600260065414156111d15760405162461bcd60e51b81526004016106d190613083565b600260065582156111ef576111e586610f9e565b9092509050611215565b604080518082019091526001600160a01b0387168152600060208201526112159061194f565b61121f8685611acd565b6112298585611baf565b846001600160a01b0316866001600160a01b0316336001600160a01b03167fa4195c37c2947bbe89165f03e320b6903116f0b10d8cfdb522330f7ce6f9fa248760405161127691906130c1565b60405180910390a46001600655909590945092505050565b6000828201838110156107875760405162461bcd60e51b81526004016106d190612fd3565b80158061133b5750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906112e99030908690600401612ee0565b60206040518083038186803b15801561130157600080fd5b505afa158015611315573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133991906127d5565b155b6113575760405162461bcd60e51b81526004016106d190613093565b6105928363095ea7b360e01b8484604051602401611376929190612f23565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611c55565b600260065414156113d05760405162461bcd60e51b81526004016106d190613083565b60026006556113dd610a83565b156113fa5760405162461bcd60e51b81526004016106d190612f93565b604080518082019091526001600160a01b0383168152600060208201526114209061194f565b61142a8282611ce4565b6114348382611d98565b816001600160a01b0316836001600160a01b03167f8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a78360405161147791906130c1565b60405180910390a35050600160065550565b805161149c90600b9060208401906123d1565b507f12e9cab73c0c48661414f76e810af7d10c67f0db958722bf9f26b28a4b4afd69816040516108b29190612f82565b80516114df90600c9060208401906123d1565b507f862f26027c5033c09d43eacd856547decb3227c210f6eb7b6bdc0cf5edaa3f4b816040516108b29190612f82565b6000805b835181101561155b5783818151811061152857fe5b60200260200101516001600160a01b0316836001600160a01b03161415611553576001915050610682565b600101611513565b5060009392505050565b61156d610409565b6115756106f7565b6001600160a01b0316633d18b9126040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156115af57600080fd5b505af11580156115c3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a979190612746565b6001600160a01b038316600081815260096020526040808220805491516370a0823160e01b8152929390926001600160801b039092169184916116a2918791906370a082319061163b903090600401612e95565b60206040518083038186803b15801561165357600080fd5b505afa158015611667573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168b91906127d5565b8554600160801b90046001600160801b0316611e5c565b90508015611714576116b4828261128e565b83546001600160801b0319166001600160801b0382161784556040519092506001600160a01b038816907f64dcb34f2e45bc61753d72992aa4199ec96717158435e8085568ff38844bcd1c9061170b9085906130c1565b60405180910390a25b6001600160a01b038781166000908152600a60209081526040808320938a1683529290522080546001600160801b03600160801b82048116965016838110156117d55781546001600160801b0319166001600160801b03851617825561178561177e898684611ea7565b879061128e565b9550886001600160a01b0316886001600160a01b03167fe3bdf7e684a4024370a702abfcca4a32b08642aadc27065f3356458bd23e977e86896040516117cc929190613105565b60405180910390a35b851561184b5781546001600160801b031682556117fc6001600160a01b038a168988611ed9565b886001600160a01b0316886001600160a01b03167fe3bdf7e684a4024370a702abfcca4a32b08642aadc27065f3356458bd23e977e8660006040516118429291906130ea565b60405180910390a35b6040516370a0823160e01b81526000906001600160a01b038b16906370a082319061187a903090600401612e95565b60206040518083038186803b15801561189257600080fd5b505afa1580156118a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ca91906127d5565b8654909150600160801b90046001600160801b03168110156119425785546001600160801b03808316600160801b0291161786556040516001600160a01b038b16907fdeb2652df61fdedd2d231dc4d777175097cc26369032d339be2a6db36d7754f2906119399084906130c1565b60405180910390a25b5050505050509392505050565b611957610a83565b61196357611963611565565b600061196d6106a5565b60085490915060005b818110156119b2576119aa6008828154811061198e57fe5b6000918252602090912001546001600160a01b03168585611ef8565b600101611976565b50505050565b6001600160a01b0383166119de5760405162461bcd60e51b81526004016106d190613043565b6001600160a01b038216611a045760405162461bcd60e51b81526004016106d190612fb3565b611a0f838383610592565b611a4c81604051806060016040528060268152602001613281602691396001600160a01b0386166000908152602081905260409020549190611157565b6001600160a01b038085166000908152602081905260408082209390935590841681522054611a7b908261128e565b6001600160a01b0380841660008181526020819052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610f919085906130c1565b6001600160a01b038216611af35760405162461bcd60e51b81526004016106d190613033565b611aff82600083610592565b611b3c8160405180606001604052806022815260200161325f602291396001600160a01b0385166000908152602081905260409020549190611157565b6001600160a01b038316600090815260208190526040902055600254611b629082611183565b6002556040516000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90611ba39085906130c1565b60405180910390a35050565b611bb76106f7565b6001600160a01b031663c32e72028260006040518363ffffffff1660e01b8152600401611be59291906130cf565b602060405180830381600087803b158015611bff57600080fd5b505af1158015611c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c379190612746565b50610d4a8282611c45610d4e565b6001600160a01b03169190611ed9565b6060611caa826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166121e99092919063ffffffff16565b8051909150156105925780806020019051810190611cc89190612746565b6105925760405162461bcd60e51b81526004016106d190613073565b6001600160a01b038216611d0a5760405162461bcd60e51b81526004016106d1906130a3565b611d1660008383610592565b600254611d23908261128e565b6002556001600160a01b038216600090815260208190526040902054611d49908261128e565b6001600160a01b0383166000818152602081905260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90611ba39085906130c1565b611db7823083611da6610d4e565b6001600160a01b03169291906121f8565b600e546040516321d0683360e11b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916343a0d06691611e0a91908590600190600401613113565b602060405180830381600087803b158015611e2457600080fd5b505af1158015611e38573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105929190612746565b6000831561155b576000611e708484611183565b90508015611e9c57611e9485611e8e83670de0b6b3a7640000612219565b90612253565b915050610787565b505060009392505050565b6000611ed1670de0b6b3a7640000611e8e611ec28686611183565b611ecb886108bd565b90612219565b949350505050565b6105928363a9059cbb60e01b8484604051602401611376929190612f23565b6001600160a01b038316600081815260096020526040808220805491516370a0823160e01b815290936001600160801b039092169291906370a0823190611f43903090600401612e95565b60206040518083038186803b158015611f5b57600080fd5b505afa158015611f6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9391906127d5565b8354909150600090611fb89086908490600160801b90046001600160801b0316611e5c565b9050801561208557611fca838261128e565b84546001600160801b0319166001600160801b0382161785556040519093506001600160a01b038816907f64dcb34f2e45bc61753d72992aa4199ec96717158435e8085568ff38844bcd1c906120219086906130c1565b60405180910390a283546001600160801b03808416600160801b0291161784556040516001600160a01b038816907fdeb2652df61fdedd2d231dc4d777175097cc26369032d339be2a6db36d7754f29061207c9085906130c1565b60405180910390a25b60005b60028110156121df57600087826002811061209f57fe5b60200201516001600160a01b031614156120b8576121d7565b6001600160a01b0388166000908152600a60205260408120818984600281106120dd57fe5b602090810291909101516001600160a01b0316825281019190915260400160002080549091506001600160801b0316858110156121d457600061214c6121348b866002811061212857fe5b60200201518985611ea7565b8454600160801b90046001600160801b03169061128e565b83546001600160801b03196001600160801b03918216600160801b84841602171690891617845590506001600160a01b038b168a856002811061218b57fe5b60200201516001600160a01b03167fe3bdf7e684a4024370a702abfcca4a32b08642aadc27065f3356458bd23e977e89846040516121ca929190613105565b60405180910390a3505b50505b600101612088565b5050505050505050565b6060611ed18484600085612285565b6119b2846323b872dd60e01b85858560405160240161137693929190612efb565b60008261222857506000610682565b8282028284828161223557fe5b04146107875760405162461bcd60e51b81526004016106d190613013565b60008082116122745760405162461bcd60e51b81526004016106d190613003565b81838161227d57fe5b049392505050565b6060824710156122a75760405162461bcd60e51b81526004016106d190612ff3565b6122b085612346565b6122cc5760405162461bcd60e51b81526004016106d190613063565b60006060866001600160a01b031685876040516122e99190612e67565b60006040518083038185875af1925050503d8060008114612326576040519150601f19603f3d011682016040523d82523d6000602084013e61232b565b606091505b509150915061233b82828661234c565b979650505050505050565b3b151590565b6060831561235b575081610787565b82511561236b5782518084602001fd5b8160405162461bcd60e51b81526004016106d19190612f82565b604080518082019091526000808252602082015290565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061241257805160ff191683800117855561243f565b8280016001018555821561243f579182015b8281111561243f578251825591602001919060010190612424565b5061244b92915061244f565b5090565b5b8082111561244b5760008155600101612450565b803561068281613238565b805161068281613238565b80356106828161324c565b80516106828161324c565b600082601f8301126124a157600080fd5b81516124b46124af82613170565b613149565b915080825260208301602083018583830111156124d057600080fd5b6124db838284613202565b50505092915050565b600060c082840312156124f657600080fd5b61250060c0613149565b9050600061250e848461246f565b825250602061251f8484830161246f565b60208301525060406125338482850161246f565b60408301525060606125478482850161246f565b606083015250608061255b8482850161246f565b60808301525060a061256f84828501612485565b60a08301525092915050565b803561068281613255565b805161068281613255565b6000602082840312156125a357600080fd5b6000611ed18484612464565b6000602082840312156125c157600080fd5b6000611ed1848461246f565b600080604083850312156125e057600080fd5b60006125ec8585612464565b92505060206125fd85828601612464565b9150509250929050565b60008060006060848603121561261c57600080fd5b60006126288686612464565b935050602061263986828701612464565b925050604061264a8682870161257b565b9150509250925092565b6000806000806080858703121561266a57600080fd5b60006126768787612464565b945050602061268787828801612464565b93505060406126988782880161257b565b92505060606126a98782880161247a565b91505092959194509250565b600080604083850312156126c857600080fd5b60006126d48585612464565b92505060206125fd8582860161257b565b6000806000606084860312156126fa57600080fd5b60006127068686612464565b93505060206127178682870161257b565b925050604061264a8682870161247a565b60006020828403121561273a57600080fd5b6000611ed1848461247a565b60006020828403121561275857600080fd5b6000611ed18484612485565b60006020828403121561277657600080fd5b815167ffffffffffffffff81111561278d57600080fd5b611ed184828501612490565b600060c082840312156127ab57600080fd5b6000611ed184846124e4565b6000602082840312156127c957600080fd5b6000611ed1848461257b565b6000602082840312156127e757600080fd5b6000611ed18484612586565b6000806040838503121561280657600080fd5b6000612812858561257b565b92505060206125fd8582860161247a565b600061282f8383612852565b505060200190565b600061282f8383612e55565b61284c816131e1565b82525050565b61284c816131b0565b60006128668261319e565b61287081856131a2565b935061287b83613198565b8060005b838110156128a95781516128938882612823565b975061289e83613198565b92505060010161287f565b509495945050505050565b60006128bf8261319e565b6128c981856131a2565b93506128d483613198565b8060005b838110156128a95781516128ec8882612837565b97506128f783613198565b9250506001016128d8565b61284c816131bb565b60006129168261319e565b61292081856131ab565b9350612930818560208601613202565b9290920192915050565b61284c816131ec565b600061294e8261319e565b61295881856131a2565b9350612968818560208601613202565b6129718161322e565b9093019392505050565b60006129886011836131a2565b7017d7d9195c1bdcda5d0e8814185d5cd959607a1b815260200192915050565b60006129b56013836131a2565b724f6e6c79206f776e65722063616c6c61626c6560681b815260200192915050565b60006129e46023836131a2565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647281526265737360e81b602082015260400192915050565b6000612a29600f836131ab565b6e022b73d3cb6b29029ba30b5b2b21d1608d1b8152600f0192915050565b6000612a546022836131a2565b7f45524332303a20617070726f766520746f20746865207a65726f206164647265815261737360f01b602082015260400192915050565b6000612a98601b836131a2565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000612ad1601e836131a2565b7f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815260200192915050565b6000612b0a6026836131a2565b7f416464726573733a20696e73756666696369656e742062616c616e636520666f8152651c8818d85b1b60d21b602082015260400192915050565b6000612b52601a836131a2565b7f536166654d6174683a206469766973696f6e206279207a65726f000000000000815260200192915050565b6000612b8b6021836131a2565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000612bce6011836131a2565b701a5b9a5d0e88125b9a5d1a585b1a5e9959607a1b815260200192915050565b6000612bfb6021836131a2565b7f45524332303a206275726e2066726f6d20746865207a65726f206164647265738152607360f81b602082015260400192915050565b6000612c3e6025836131a2565b7f45524332303a207472616e736665722066726f6d20746865207a65726f206164815264647265737360d81b602082015260400192915050565b6000612c856003836131ab565b6273746b60e81b815260030192915050565b6000612ca46024836131a2565b7f45524332303a20617070726f76652066726f6d20746865207a65726f206164648152637265737360e01b602082015260400192915050565b6000612cea601d836131a2565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000815260200192915050565b6000612d23602a836131a2565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e8152691bdd081cdd58d8d9595960b21b602082015260400192915050565b6000612d6f601f836131a2565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00815260200192915050565b6000612da86036836131a2565b7f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f81527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b602082015260400192915050565b6000612e00601f836131a2565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300815260200192915050565b80516040830190612e3d8482612e4c565b5060208201516119b260208501825b61284c816131c0565b61284c816131d8565b61284c816131db565b6000610787828461290b565b6000612e7e82612a1c565b9150610787828461290b565b6000612e7e82612c78565b602081016106828284612852565b60608101612eb18286612843565b8181036020830152612ec3818561285b565b90508181036040830152612ed781846128b4565b95945050505050565b60408101612eee8285612852565b6107876020830184612852565b60608101612f098286612852565b612f166020830185612852565b611ed16040830184612e55565b60408101612f318285612852565b6107876020830184612e55565b60208082528101610787818461285b565b60408082528101612f60818561285b565b90508181036020830152611ed181846128b4565b602081016106828284612902565b602080825281016107878184612943565b602080825281016106828161297b565b60208082528101610682816129a8565b60208082528101610682816129d7565b6020808252810161068281612a47565b6020808252810161068281612a8b565b6020808252810161068281612ac4565b6020808252810161068281612afd565b6020808252810161068281612b45565b6020808252810161068281612b7e565b6020808252810161068281612bc1565b6020808252810161068281612bee565b6020808252810161068281612c31565b6020808252810161068281612c97565b6020808252810161068281612cdd565b6020808252810161068281612d16565b6020808252810161068281612d62565b6020808252810161068281612d9b565b6020808252810161068281612df3565b604081016106828284612e2c565b602081016106828284612e55565b604081016130dd8285612e55565b6107876020830184612902565b604081016130f88285612e55565b610787602083018461293a565b60408101612f318285612e55565b606081016131218286612e55565b61312e6020830185612e55565b611ed16040830184612902565b602081016106828284612e5e565b60405181810167ffffffffffffffff8111828210171561316857600080fd5b604052919050565b600067ffffffffffffffff82111561318757600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b919050565b6000610682826131cc565b151590565b6001600160801b031690565b6001600160a01b031690565b90565b60ff1690565b6000610682826131f7565b6000610682826131d8565b6000610682826131b0565b60005b8381101561321d578181015183820152602001613205565b838111156119b25750506000910152565b601f01601f191690565b613241816131b0565b8114610a9757600080fd5b613241816131bb565b613241816131d856fe45524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220beb03a49a663a5c5bc955d20932772d8faaacb4d38a5916285fc9db4a02465f664736f6c634300060c0033000000000000000000000000c3dc853dd716bd5754f421ef94fdcbac3902ab32000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae31000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd520000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b

Deployed Bytecode

0x60806040523480156200001157600080fd5b5060043610620000a05760003560e01c80638aeb8b84116200006f5780638aeb8b84146200010c57806397b02f0c146200012557806398a7c4c7146200013c578063a5e387511462000146578063d0ac1a8d146200015d57620000a0565b80630c0872f514620000a55780631c25201c14620000d4578063456b4a3714620000eb578063893d20e81462000102575b600080fd5b620000bc620000b636600462000866565b62000174565b604051620000cb919062000a05565b60405180910390f35b620000bc620000e53660046200089f565b620001fd565b620000bc620000fc366004620007d6565b62000218565b620000bc62000236565b620001236200011d36600462000820565b620002d2565b005b620001236200013636600462000820565b620003bc565b620000bc62000498565b620000bc620001573660046200089f565b620004a7565b620001236200016e366004620007d6565b62000650565b600081306040516200018690620006f3565b6200019392919062000a6f565b604051809103906000f080158015620001b0573d6000803e3d6000fd5b509050336001600160a01b03167f8f1eab90fee81d885732c2e07a3a9b9f6c32278756471123fa57486858c3f7018284604051620001f092919062000a3b565b60405180910390a2919050565b6000908152600160205260409020546001600160a01b031690565b6001600160a01b039081166000908152600260205260409020541690565b60007f000000000000000000000000c3dc853dd716bd5754f421ef94fdcbac3902ab326001600160a01b031663893d20e86040518163ffffffff1660e01b815260040160206040518083038186803b1580156200029257600080fd5b505afa158015620002a7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002cd9190620007ff565b905090565b620002dc62000236565b6001600160a01b0316336001600160a01b031614620003185760405162461bcd60e51b81526004016200030f9062000a93565b60405180910390fd5b60005b81811015620003b7578282828181106200033157fe5b9050602002016020810190620003489190620007d6565b6001600160a01b03166357d159c660006040518263ffffffff1660e01b815260040162000376919062000a5f565b600060405180830381600087803b1580156200039157600080fd5b505af1158015620003a6573d6000803e3d6000fd5b5050600190920191506200031b9050565b505050565b620003c662000236565b6001600160a01b0316336001600160a01b031614620003f95760405162461bcd60e51b81526004016200030f9062000a93565b60005b81811015620003b7578282828181106200041257fe5b9050602002016020810190620004299190620007d6565b6001600160a01b03166357d159c660016040518263ffffffff1660e01b815260040162000457919062000a5f565b600060405180830381600087803b1580156200047257600080fd5b505af115801562000487573d6000803e3d6000fd5b505060019092019150620003fc9050565b6000546001600160a01b031690565b600080620004b583620001fd565b6001600160a01b031614620004de5760405162461bcd60e51b81526004016200030f9062000aa5565b606063b7b0422d60e01b83604051602401620004fb919062000ac9565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290506200053a8162000174565b600084815260016020908152604080832080546001600160a01b0319166001600160a01b0386169081179091558151632e266ba960e21b81529151949650929363b899aea492600480840193919291829003018186803b1580156200059e57600080fd5b505afa158015620005b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005d99190620007ff565b6001600160a01b038481166000908152600260205260409081902080546001600160a01b031916928416929092179091555190915084907fe547dfbae0eb3c2c335669b841fa076eb2a1a64eab9bd2fb49868b5e624586f19062000641908690859062000a15565b60405180910390a25050919050565b6200065a62000236565b6001600160a01b0316336001600160a01b0316146200068d5760405162461bcd60e51b81526004016200030f9062000ab7565b62000698816200069b565b50565b600080546001600160a01b0319166001600160a01b0383161790556040517f9007c6c0b3b1622c381c19563620e3ce8b824e958af3020495bd6654e8a0310a90620006e890839062000a05565b60405180910390a150565b6103a78062000bc483390190565b80356200070e8162000ba1565b92915050565b80516200070e8162000ba1565b60008083601f8401126200073457600080fd5b50813567ffffffffffffffff8111156200074d57600080fd5b6020830191508360208202830111156200076657600080fd5b9250929050565b600082601f8301126200077f57600080fd5b813562000796620007908262000b01565b62000ad9565b91508082526020830160208301858383011115620007b357600080fd5b620007c083828462000b58565b50505092915050565b80356200070e8162000bb8565b600060208284031215620007e957600080fd5b6000620007f7848462000701565b949350505050565b6000602082840312156200081257600080fd5b6000620007f7848462000714565b600080602083850312156200083457600080fd5b823567ffffffffffffffff8111156200084c57600080fd5b6200085a8582860162000721565b92509250509250929050565b6000602082840312156200087957600080fd5b813567ffffffffffffffff8111156200089157600080fd5b620007f7848285016200076d565b600060208284031215620008b257600080fd5b6000620007f78484620007c9565b620008cb8162000b37565b82525050565b620008cb8162000b44565b6000620008e98262000b2a565b620008f5818562000b2e565b93506200090781856020860162000b64565b620009128162000b97565b9093019392505050565b60006200092b60258362000b2e565b7f4f6e6c7920746865206f776e65722063616e2063616c6c20746869732066756e81526431ba34b7b760d91b602082015260400192915050565b600062000974601e8362000b2e565b7f6465706c6f793a205772617070657220616c7265616479206578697374730000815260200192915050565b6000620009af60368362000b2e565b7f73657443616e6f6e6963616c4c69623a204f6e6c7920746865206f776e65722081527531b0b71031b0b636103a3434b990333ab731ba34b7b760511b602082015260400192915050565b620008cb8162000b55565b602081016200070e8284620008c0565b6040810162000a258285620008c0565b62000a346020830184620008c0565b9392505050565b6040810162000a4b8285620008c0565b8181036020830152620007f78184620008dc565b602081016200070e8284620008d1565b6040808252810162000a828185620008dc565b905062000a346020830184620008c0565b602080825281016200070e816200091c565b602080825281016200070e8162000965565b602080825281016200070e81620009a0565b602081016200070e8284620009fa565b60405181810167ffffffffffffffff8111828210171562000af957600080fd5b604052919050565b600067ffffffffffffffff82111562000b1957600080fd5b506020601f91909101601f19160190565b5190565b90815260200190565b60006200070e8262000b49565b151590565b6001600160a01b031690565b90565b82818337506000910152565b60005b8381101562000b8157818101518382015260200162000b67565b8381111562000b91576000848401525b50505050565b601f01601f191690565b62000bac8162000b37565b81146200069857600080fd5b62000bac8162000b5556fe60a060405234801561001057600080fd5b506040516103a73803806103a78339818101604052604081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040818152602092830151606081811b6001600160601b0319166080526398a7c4c760e01b845291519095506000945090926001600160a01b038616926398a7c4c79260048083019392829003018186803b15801561013b57600080fd5b505afa15801561014f573d6000803e3d6000fd5b505050506040513d602081101561016557600080fd5b505160405185516001600160a01b0390921691869190819060208401908083835b602083106101a55780518252601f199092019160209182019101610186565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855af49150503d8060008114610205576040519150601f19603f3d011682016040523d82523d6000602084013e61020a565b606091505b509150915081819061029a5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561025f578181015183820152602001610247565b50505050905090810190601f16801561028c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050505060805160601c60ee6102b960003980600e525060ee6000f3fe608060405236600a57005b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166398a7c4c76040518163ffffffff1660e01b815260040160206040518083038186803b158015606457600080fd5b505afa1580156077573d6000803e3d6000fd5b505050506040513d6020811015608c57600080fd5b505190503660008037600080366000846127105a03f43d806000803e81801560b357816000f35b816000fdfea26469706673582212209a2062a062ba495cf18ec0c812d8c737e96be9cba7dae39d1b5ab98efe85b85764736f6c634300060c0033a264697066735822122032d315117304e289ab7dc4c6ed7d722c289d06698e282c86a8c6bcae2d42e7e164736f6c634300060c0033

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

000000000000000000000000c3dc853dd716bd5754f421ef94fdcbac3902ab32000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae31000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd520000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b

-----Decoded View---------------
Arg [0] : _dispatcher (address): 0xC3DC853dD716bd5754f421ef94fdCbac3902ab32
Arg [1] : _convexBooster (address): 0xF403C135812408BFbE8713b5A23a04b3D48AAE31
Arg [2] : _crvToken (address): 0xD533a949740bb3306d119CC777fa900bA034cd52
Arg [3] : _cvxToken (address): 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000c3dc853dd716bd5754f421ef94fdcbac3902ab32
Arg [1] : 000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae31
Arg [2] : 000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd52
Arg [3] : 0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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