ETH Price: $3,388.57 (-0.96%)

Contract

0xe567B3174af8eA368ed536998a597147Ec29De8f
 
Transaction Hash
Method
Block
From
To
Withdraw212125952024-11-18 5:45:118 days ago1731908711IN
0xe567B317...7Ec29De8f
0 ETH0.00865739.51379872
Deposit211781742024-11-13 10:28:1113 days ago1731493691IN
0xe567B317...7Ec29De8f
0 ETH0.003989119.33322535
Deposit211780182024-11-13 9:56:5913 days ago1731491819IN
0xe567B317...7Ec29De8f
0 ETH0.0077829319.23364236
Approve211775722024-11-13 8:27:3513 days ago1731486455IN
0xe567B317...7Ec29De8f
0 ETH0.0008347617.79382999
Deposit211775642024-11-13 8:25:5913 days ago1731486359IN
0xe567B317...7Ec29De8f
0 ETH0.0036742217.80718913
Withdraw187697722023-12-12 11:21:23350 days ago1702380083IN
0xe567B317...7Ec29De8f
0 ETH0.0255496330.91787411
Approve187690852023-12-12 9:02:35350 days ago1702371755IN
0xe567B317...7Ec29De8f
0 ETH0.0010559935.42052989
Transfer187690572023-12-12 8:56:59350 days ago1702371419IN
0xe567B317...7Ec29De8f
0 ETH0.0017210832.84195545
Approve187689842023-12-12 8:42:23350 days ago1702370543IN
0xe567B317...7Ec29De8f
0 ETH0.0012823527.13356884
Approve187689072023-12-12 8:26:59350 days ago1702369619IN
0xe567B317...7Ec29De8f
0 ETH0.0009441331.68131748
Withdraw182896942023-10-06 6:32:23417 days ago1696573943IN
0xe567B317...7Ec29De8f
0 ETH0.0045745.55488173
Approve182896432023-10-06 6:22:11417 days ago1696573331IN
0xe567B317...7Ec29De8f
0 ETH0.000309546.54959008
Approve182896132023-10-06 6:15:59417 days ago1696572959IN
0xe567B317...7Ec29De8f
0 ETH0.000268155.67391936
Withdraw168103582023-03-12 7:08:11625 days ago1678604891IN
0xe567B317...7Ec29De8f
0 ETH0.0148158517.99304866
Withdraw168102462023-03-12 6:45:11625 days ago1678603511IN
0xe567B317...7Ec29De8f
0 ETH0.0035786620.85843521
Withdraw157604442022-10-16 11:41:35772 days ago1665920495IN
0xe567B317...7Ec29De8f
0 ETH0.0107526613.04560861
Withdraw137900672021-12-12 11:38:141080 days ago1639309094IN
0xe567B317...7Ec29De8f
0 ETH0.0295455435.09593202
Withdraw134848762021-10-25 6:09:291128 days ago1635142169IN
0xe567B317...7Ec29De8f
0 ETH0.0487388659.65582184
Withdraw134814832021-10-24 17:25:361129 days ago1635096336IN
0xe567B317...7Ec29De8f
0 ETH0.0742479689.01555608
Withdraw123196512021-04-27 2:15:051310 days ago1619489705IN
0xe567B317...7Ec29De8f
0 ETH0.032957240
Withdraw123077502021-04-25 6:12:401311 days ago1619331160IN
0xe567B317...7Ec29De8f
0 ETH0.0290463136
Withdraw122772662021-04-20 13:24:251316 days ago1618925065IN
0xe567B317...7Ec29De8f
0 ETH0.04063445238
Withdraw122297202021-04-13 4:58:331323 days ago1618289913IN
0xe567B317...7Ec29De8f
0 ETH0.0742611264
Withdraw121383952021-03-30 4:11:541338 days ago1617077514IN
0xe567B317...7Ec29De8f
0 ETH0.0210266395
Withdraw121258572021-03-28 5:55:271339 days ago1616910927IN
0xe567B317...7Ec29De8f
0 ETH0.092826480.00000134
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
gWBTC

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion, GNU GPLv3 license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2020-11-29
*/

// SPDX-License-Identifier: GPL-3.0-only
pragma experimental ABIEncoderV2;

// File: @openzeppelin/contracts/GSN/Context.sol


pragma solidity ^0.6.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: @openzeppelin/contracts/access/Ownable.sol


pragma solidity ^0.6.0;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(_owner == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

// File: @openzeppelin/contracts/token/ERC20/IERC20.sol


pragma solidity ^0.6.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: @openzeppelin/contracts/math/SafeMath.sol


pragma solidity ^0.6.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, 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) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * 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);
        uint256 c = a - b;

        return c;
    }

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

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts 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) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts 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) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

// File: @openzeppelin/contracts/utils/Address.sol


pragma solidity ^0.6.2;

/**
 * @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 in 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");
        return _functionCallWithValue(target, data, value, errorMessage);
    }

    function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
        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: @openzeppelin/contracts/token/ERC20/ERC20.sol


pragma solidity ^0.6.0;





/**
 * @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;
    using Address for address;

    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 returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view 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 returns (uint8) {
        return _decimals;
    }

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

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view 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 {
        _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: @openzeppelin/contracts/utils/ReentrancyGuard.sol


pragma solidity ^0.6.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].
 */
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: contracts/GToken.sol

pragma solidity ^0.6.0;


/**
 * @dev Minimal interface for gTokens, implemented by the GTokenBase contract.
 *      See GTokenBase.sol for further documentation.
 */
interface GToken is IERC20
{
	// pure functions
	function calcDepositSharesFromCost(uint256 _cost, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee) external pure returns (uint256 _netShares, uint256 _feeShares);
	function calcDepositCostFromShares(uint256 _netShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee) external pure returns (uint256 _cost, uint256 _feeShares);
	function calcWithdrawalSharesFromCost(uint256 _cost, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee) external pure returns (uint256 _grossShares, uint256 _feeShares);
	function calcWithdrawalCostFromShares(uint256 _grossShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee) external pure returns (uint256 _cost, uint256 _feeShares);

	// view functions
	function reserveToken() external view returns (address _reserveToken);
	function totalReserve() external view returns (uint256 _totalReserve);
	function depositFee() external view returns (uint256 _depositFee);
	function withdrawalFee() external view returns (uint256 _withdrawalFee);

	// open functions
	function deposit(uint256 _cost) external;
	function withdraw(uint256 _grossShares) external;
}

// File: contracts/GPooler.sol

pragma solidity ^0.6.0;


/**
 * @dev An interface to extend gTokens with locked liquidity pools.
 *      See GTokenBase.sol for further documentation.
 */
interface GPooler
{
	// view functions
	function stakesToken() external view returns (address _stakesToken);
	function liquidityPool() external view returns (address _liquidityPool);
	function liquidityPoolBurningRate() external view returns (uint256 _burningRate);
	function liquidityPoolLastBurningTime() external view returns (uint256 _lastBurningTime);
	function liquidityPoolMigrationRecipient() external view returns (address _migrationRecipient);
	function liquidityPoolMigrationUnlockTime() external view returns (uint256 _migrationUnlockTime);

	// priviledged functions
	function allocateLiquidityPool(uint256 _stakesAmount, uint256 _sharesAmount) external;
	function setLiquidityPoolBurningRate(uint256 _burningRate) external;
	function burnLiquidityPoolPortion() external;
	function initiateLiquidityPoolMigration(address _migrationRecipient) external;
	function cancelLiquidityPoolMigration() external;
	function completeLiquidityPoolMigration() external;

	// emitted events
	event BurnLiquidityPoolPortion(uint256 _stakesAmount, uint256 _sharesAmount);
	event InitiateLiquidityPoolMigration(address indexed _migrationRecipient);
	event CancelLiquidityPoolMigration(address indexed _migrationRecipient);
	event CompleteLiquidityPoolMigration(address indexed _migrationRecipient, uint256 _stakesAmount, uint256 _sharesAmount);
}

// File: contracts/GFormulae.sol

pragma solidity ^0.6.0;


/**
 * @dev Pure implementation of deposit/minting and withdrawal/burning formulas
 *      for gTokens.
 *      All operations assume that, if total supply is 0, then the total
 *      reserve is also 0, and vice-versa.
 *      Fees are calculated percentually based on the gross amount.
 *      See GTokenBase.sol for further documentation.
 */
library GFormulae
{
	using SafeMath for uint256;

	/* deposit(cost):
	 *   price = reserve / supply
	 *   gross = cost / price
	 *   net = gross * 0.99	# fee is assumed to be 1% for simplicity
	 *   fee = gross - net
	 *   return net, fee
	 */
	function _calcDepositSharesFromCost(uint256 _cost, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee) internal pure returns (uint256 _netShares, uint256 _feeShares)
	{
		uint256 _grossShares = _totalSupply == _totalReserve ? _cost : _cost.mul(_totalSupply).div(_totalReserve);
		_netShares = _grossShares.mul(uint256(1e18).sub(_depositFee)).div(1e18);
		_feeShares = _grossShares.sub(_netShares);
		return (_netShares, _feeShares);
	}

	/* deposit_reverse(net):
	 *   price = reserve / supply
	 *   gross = net / 0.99	# fee is assumed to be 1% for simplicity
	 *   cost = gross * price
	 *   fee = gross - net
	 *   return cost, fee
	 */
	function _calcDepositCostFromShares(uint256 _netShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee) internal pure returns (uint256 _cost, uint256 _feeShares)
	{
		uint256 _grossShares = _netShares.mul(1e18).div(uint256(1e18).sub(_depositFee));
		_cost = _totalReserve == _totalSupply ? _grossShares : _grossShares.mul(_totalReserve).div(_totalSupply);
		_feeShares = _grossShares.sub(_netShares);
		return (_cost, _feeShares);
	}

	/* withdrawal_reverse(cost):
	 *   price = reserve / supply
	 *   net = cost / price
	 *   gross = net / 0.99	# fee is assumed to be 1% for simplicity
	 *   fee = gross - net
	 *   return gross, fee
	 */
	function _calcWithdrawalSharesFromCost(uint256 _cost, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee) internal pure returns (uint256 _grossShares, uint256 _feeShares)
	{
		uint256 _netShares = _cost == _totalReserve ? _totalSupply : _cost.mul(_totalSupply).div(_totalReserve);
		_grossShares = _netShares.mul(1e18).div(uint256(1e18).sub(_withdrawalFee));
		_feeShares = _grossShares.sub(_netShares);
		return (_grossShares, _feeShares);
	}

	/* withdrawal(gross):
	 *   price = reserve / supply
	 *   net = gross * 0.99	# fee is assumed to be 1% for simplicity
	 *   cost = net * price
	 *   fee = gross - net
	 *   return cost, fee
	 */
	function _calcWithdrawalCostFromShares(uint256 _grossShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee) internal pure returns (uint256 _cost, uint256 _feeShares)
	{
		uint256 _netShares = _grossShares.mul(uint256(1e18).sub(_withdrawalFee)).div(1e18);
		_cost = _netShares == _totalSupply ? _totalReserve : _netShares.mul(_totalReserve).div(_totalSupply);
		_feeShares = _grossShares.sub(_netShares);
		return (_cost, _feeShares);
	}
}

// File: contracts/modules/Math.sol

pragma solidity ^0.6.0;

/**
 * @dev This library implements auxiliary math definitions.
 */
library Math
{
	function _min(uint256 _amount1, uint256 _amount2) internal pure returns (uint256 _minAmount)
	{
		return _amount1 < _amount2 ? _amount1 : _amount2;
	}

	function _max(uint256 _amount1, uint256 _amount2) internal pure returns (uint256 _maxAmount)
	{
		return _amount1 > _amount2 ? _amount1 : _amount2;
	}
}

// File: contracts/interop/WrappedEther.sol

pragma solidity ^0.6.0;


/**
 * @dev Minimal set of declarations for WETH interoperability.
 */
interface WETH is IERC20
{
	function deposit() external payable;
	function withdraw(uint256 _amount) external;
}

// File: contracts/network/$.sol

pragma solidity ^0.6.0;

/**
 * @dev This library is provided for conveniece. It is the single source for
 *      the current network and all related hardcoded contract addresses. It
 *      also provide useful definitions for debuging faultless code via events.
 */
library $
{
	address constant stkGRO = 0xD93f98b483CC2F9EFE512696DF8F5deCB73F9497;

	address constant GRO = 0x09e64c2B61a5f1690Ee6fbeD9baf5D6990F8dFd0;

	address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;

	address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

	address constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;

	address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

	address constant cDAI = 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643;

	address constant cUSDC = 0x39AA39c021dfbaE8faC545936693aC917d5E7563;

	address constant cETH = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;

	address constant cWBTC = 0xC11b1268C1A384e55C48c2391d8d480264A3A7F4;

	address constant COMP = 0xc00e94Cb662C3520282E6f5717214004A7f26888;

	address constant Aave_AAVE_LENDING_POOL = 0x398eC7346DcD622eDc5ae82352F02bE94C62d119;

	address constant Aave_AAVE_LENDING_POOL_CORE = 0x3dfd23A6c5E8BbcFc9581d2E864a68feb6a076d3;

	address constant Balancer_FACTORY = 0x9424B1412450D0f8Fc2255FAf6046b98213B76Bd;

	address constant Compound_COMPTROLLER = 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B;

	address constant Dydx_SOLO_MARGIN = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;
}

// File: contracts/modules/Wrapping.sol

pragma solidity ^0.6.0;



/**
 * @dev This library abstracts Wrapped Ether operations.
 */
library Wrapping
{
	/**
	 * @dev Sends some ETH to the Wrapped Ether contract in exchange for WETH.
	 * @param _amount The amount of ETH to be wrapped in WETH.
	 * @return _success A boolean indicating whether or not the operation suceeded.
	 */
	function _wrap(uint256 _amount) internal returns (bool _success)
	{
		try WETH($.WETH).deposit{value: _amount}() {
			return true;
		} catch (bytes memory /* _data */) {
			return false;
		}
	}

	/**
	 * @dev Receives some ETH from the Wrapped Ether contract in exchange for WETH.
	 *      Note that the contract using this library function must declare a
	 *      payable receive/fallback function.
	 * @param _amount The amount of ETH to be wrapped in WETH.
	 * @return _success A boolean indicating whether or not the operation suceeded.
	 */
	function _unwrap(uint256 _amount) internal returns (bool _success)
	{
		try WETH($.WETH).withdraw(_amount) {
			return true;
		} catch (bytes memory /* _data */) {
			return false;
		}
	}

	/**
	 * @dev Sends some ETH to the Wrapped Ether contract in exchange for WETH.
	 *      This operation will revert if it does not succeed.
	 * @param _amount The amount of ETH to be wrapped in WETH.
	 */
	function _safeWrap(uint256 _amount) internal
	{
		require(_wrap(_amount), "wrap failed");
	}

	/**
	 * @dev Receives some ETH from the Wrapped Ether contract in exchange for WETH.
	 *      This operation will revert if it does not succeed. Note that
	 *      the contract using this library function must declare a payable
	 *      receive/fallback function.
	 * @param _amount The amount of ETH to be wrapped in WETH.
	 */
	function _safeUnwrap(uint256 _amount) internal
	{
		require(_unwrap(_amount), "unwrap failed");
	}
}

// File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol


pragma solidity ^0.6.0;




/**
 * @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: contracts/modules/Transfers.sol

pragma solidity ^0.6.0;



/**
 * @dev This library abstracts ERC-20 operations.
 */
library Transfers
{
	using SafeERC20 for IERC20;

	/**
	 * @dev Retrieves a given ERC-20 token balance for the current contract.
	 * @param _token An ERC-20 compatible token address.
	 * @return _balance The current contract balance of the given ERC-20 token.
	 */
	function _getBalance(address _token) internal view returns (uint256 _balance)
	{
		return IERC20(_token).balanceOf(address(this));
	}

	/**
	 * @dev Allows a spender to access a given ERC-20 balance for the current contract.
	 * @param _token An ERC-20 compatible token address.
	 * @param _to The spender address.
	 * @param _amount The exact spending allowance amount.
	 */
	function _approveFunds(address _token, address _to, uint256 _amount) internal
	{
		uint256 _allowance = IERC20(_token).allowance(address(this), _to);
		if (_allowance > _amount) {
			IERC20(_token).safeDecreaseAllowance(_to, _allowance - _amount);
		}
		else
		if (_allowance < _amount) {
			IERC20(_token).safeIncreaseAllowance(_to, _amount - _allowance);
		}
	}

	/**
	 * @dev Transfer a given ERC-20 token amount into the current contract.
	 * @param _token An ERC-20 compatible token address.
	 * @param _from The source address.
	 * @param _amount The amount to be transferred.
	 */
	function _pullFunds(address _token, address _from, uint256 _amount) internal
	{
		if (_amount == 0) return;
		IERC20(_token).safeTransferFrom(_from, address(this), _amount);
	}

	/**
	 * @dev Transfer a given ERC-20 token amount from the current contract.
	 * @param _token An ERC-20 compatible token address.
	 * @param _to The target address.
	 * @param _amount The amount to be transferred.
	 */
	function _pushFunds(address _token, address _to, uint256 _amount) internal
	{
		if (_amount == 0) return;
		IERC20(_token).safeTransfer(_to, _amount);
	}
}

// File: contracts/GExchange.sol

pragma solidity ^0.6.0;

/**
 * @dev Custom and uniform interface to a decentralized exchange. It is used
 *      to estimate and convert funds whenever necessary. This furnishes
 *      client contracts with the flexibility to replace conversion strategy
 *      and routing, dynamically, by delegating these operations to different
 *      external contracts that share this common interface. See
 *      GUniswapV2Exchange.sol for further documentation.
 */
interface GExchange
{
	// view functions
	function calcConversionOutputFromInput(address _from, address _to, uint256 _inputAmount) external view returns (uint256 _outputAmount);
	function calcConversionInputFromOutput(address _from, address _to, uint256 _outputAmount) external view returns (uint256 _inputAmount);

	// open functions
	function convertFunds(address _from, address _to, uint256 _inputAmount, uint256 _minOutputAmount) external returns (uint256 _outputAmount);
}

// File: contracts/modules/Conversions.sol

pragma solidity ^0.6.0;





library Conversions
{
	function _dynamicConvertFunds(address _exchange, address _from, address _to, uint256 _inputAmount, uint256 _minOutputAmount) internal returns (uint256 _outputAmount)
	{
		Transfers._approveFunds(_from, _exchange, _inputAmount);
		try GExchange(_exchange).convertFunds(_from, _to, _inputAmount, _minOutputAmount) returns (uint256 _outAmount) {
			return _outAmount;
		} catch (bytes memory /* _data */) {
			Transfers._approveFunds(_from, _exchange, 0);
			return 0;
		}
	}
}

// File: contracts/interop/Aave.sol

pragma solidity ^0.6.0;


/**
 * @dev Minimal set of declarations for Aave interoperability.
 */
interface LendingPoolAddressesProvider
{
	function getLendingPool() external view returns (address _pool);
	function getLendingPoolCore() external view returns (address payable _lendingPoolCore);
	function getPriceOracle() external view returns (address _priceOracle);
}

interface LendingPool
{
	function getReserveConfigurationData(address _reserve) external view returns (uint256 _ltv, uint256 _liquidationThreshold, uint256 _liquidationBonus, address _interestRateStrategyAddress, bool _usageAsCollateralEnabled, bool _borrowingEnabled, bool _stableBorrowRateEnabled, bool _isActive);
	function getUserAccountData(address _user) external view returns (uint256 _totalLiquidityETH, uint256 _totalCollateralETH, uint256 _totalBorrowsETH, uint256 _totalFeesETH, uint256 _availableBorrowsETH, uint256 _currentLiquidationThreshold, uint256 _ltv, uint256 _healthFactor);
	function getUserReserveData(address _reserve, address _user) external view returns (uint256 _currentATokenBalance, uint256 _currentBorrowBalance, uint256 _principalBorrowBalance, uint256 _borrowRateMode, uint256 _borrowRate, uint256 _liquidityRate, uint256 _originationFee, uint256 _variableBorrowIndex, uint256 _lastUpdateTimestamp, bool _usageAsCollateralEnabled);
	function deposit(address _reserve, uint256 _amount, uint16 _referralCode) external payable;
	function borrow(address _reserve, uint256 _amount, uint256 _interestRateMode, uint16 _referralCode) external;
	function repay(address _reserve, uint256 _amount, address payable _onBehalfOf) external payable;
	function flashLoan(address _receiver, address _reserve, uint256 _amount, bytes calldata _params) external;
}

interface LendingPoolCore
{
	function getReserveDecimals(address _reserve) external view returns (uint256 _decimals);
	function getReserveAvailableLiquidity(address _reserve) external view returns (uint256 _availableLiquidity);
}

interface AToken is IERC20
{
	function underlyingAssetAddress() external view returns (address _underlyingAssetAddress);
	function redeem(uint256 _amount) external;
}

interface APriceOracle
{
	function getAssetPrice(address _asset) external view returns (uint256 _assetPrice);
}

interface FlashLoanReceiver
{
	function executeOperation(address _reserve, uint256 _amount, uint256 _fee, bytes calldata _params) external;
}

// File: contracts/modules/AaveFlashLoanAbstraction.sol

pragma solidity ^0.6.0;





/**
 * @dev This library abstracts the Aave flash loan functionality. It has a
 *      standardized flash loan interface. See GFlashBorrower.sol,
 *      FlashLoans.sol, and DydxFlashLoanAbstraction.sol for further documentation.
 */
library AaveFlashLoanAbstraction
{
	using SafeMath for uint256;

	uint256 constant FLASH_LOAN_FEE_RATIO = 9e14; // 0.09%

	/**
	 * @dev Estimates the flash loan fee given the reserve token and required amount.
	 * @param _token The ERC-20 token to flash borrow from.
	 * @param _netAmount The amount to be borrowed without considering repay fees.
	 * @param _feeAmount the expected fee to be payed in excees of the loan amount.
	 */
	function _estimateFlashLoanFee(address _token, uint256 _netAmount) internal pure returns (uint256 _feeAmount)
	{
		_token; // silences warnings
		return _netAmount.mul(FLASH_LOAN_FEE_RATIO).div(1e18);
	}

	/**
	 * @dev Retrieves the current market liquidity for a given reserve.
	 * @param _token The reserve token to flash borrow from.
	 * @return _liquidityAmount The reserve token available market liquidity.
	 */
	function _getFlashLoanLiquidity(address _token) internal view returns (uint256 _liquidityAmount)
	{
		address _core = $.Aave_AAVE_LENDING_POOL_CORE;
		return LendingPoolCore(_core).getReserveAvailableLiquidity(_token);
	}

	/**
	 * @dev Triggers a flash loan. The current contract will receive a call
	 *      back with the loan amount and should repay it, including fees,
	 *      before returning. See GFlashBorrow.sol.
	 * @param _token The reserve token to flash borrow from.
	 * @param _netAmount The amount to be borrowed without considering repay fees.
	 * @param _context Additional data to be passed to the call back.
	 * @return _success A boolean indicating whether or not the operation suceeded.
         */
	function _requestFlashLoan(address _token, uint256 _netAmount, bytes memory _context) internal returns (bool _success)
	{
		address _pool = $.Aave_AAVE_LENDING_POOL;
		try LendingPool(_pool).flashLoan(address(this), _token, _netAmount, _context) {
			return true;
		} catch (bytes memory /* _data */) {
			return false;
		}
	}

	/**
	 * @dev This function should be called as the final step of the flash
	 *      loan to properly implement the repay of the loan.
	 * @param _token The reserve token.
	 * @param _grossAmount The amount to be repayed including repay fees.
	 */
	function _paybackFlashLoan(address _token, uint256 _grossAmount) internal
	{
		address _poolCore = $.Aave_AAVE_LENDING_POOL_CORE;
		Transfers._pushFunds(_token, _poolCore, _grossAmount);
	}
}

// File: contracts/interop/Dydx.sol

pragma solidity ^0.6.0;

/**
 * @dev Minimal set of declarations for Dydx interoperability.
 */
interface SoloMargin
{
	function getMarketTokenAddress(uint256 _marketId) external view returns (address _token);
	function getNumMarkets() external view returns (uint256 _numMarkets);
	function operate(Account.Info[] memory _accounts, Actions.ActionArgs[] memory _actions) external;
}

interface ICallee
{
	function callFunction(address _sender, Account.Info memory _accountInfo, bytes memory _data) external;
}

library Account
{
	struct Info {
		address owner;
		uint256 number;
	}
}

library Actions
{
	enum ActionType { Deposit, Withdraw, Transfer, Buy, Sell, Trade, Liquidate, Vaporize, Call }

	struct ActionArgs {
		ActionType actionType;
		uint256 accountId;
		Types.AssetAmount amount;
		uint256 primaryMarketId;
		uint256 secondaryMarketId;
		address otherAddress;
		uint256 otherAccountId;
		bytes data;
	}
}

library Types
{
	enum AssetDenomination { Wei, Par }
	enum AssetReference { Delta, Target }

	struct AssetAmount {
		bool sign;
		AssetDenomination denomination;
		AssetReference ref;
		uint256 value;
	}
}

// File: contracts/modules/DydxFlashLoanAbstraction.sol

pragma solidity ^0.6.0;






/**
 * @dev This library abstracts the Dydx flash loan functionality. It has a
 *      standardized flash loan interface. See GFlashBorrower.sol,
 *      FlashLoans.sol, and AaveFlashLoanAbstraction.sol for further documentation.
 */
library DydxFlashLoanAbstraction
{
	using SafeMath for uint256;

	/**
	 * @dev Estimates the flash loan fee given the reserve token and required amount.
	 * @param _token The ERC-20 token to flash borrow from.
	 * @param _netAmount The amount to be borrowed without considering repay fees.
	 * @param _feeAmount the expected fee to be payed in excees of the loan amount.
	 */
	function _estimateFlashLoanFee(address _token, uint256 _netAmount) internal pure returns (uint256 _feeAmount)
	{
		_token; _netAmount; // silences warnings
		return 2; // dydx has no fees, 2 wei is just a recommendation
	}

	/**
	 * @dev Retrieves the current market liquidity for a given reserve.
	 * @param _token The reserve token to flash borrow from.
	 * @return _liquidityAmount The reserve token available market liquidity.
	 */
	function _getFlashLoanLiquidity(address _token) internal view returns (uint256 _liquidityAmount)
	{
		address _solo = $.Dydx_SOLO_MARGIN;
		return IERC20(_token).balanceOf(_solo);
	}

	/**
	 * @dev Triggers a flash loan. The current contract will receive a call
	 *      back with the loan amount and should repay it, including fees,
	 *      before returning. See GFlashBorrow.sol.
	 * @param _token The reserve token to flash borrow from.
	 * @param _netAmount The amount to be borrowed without considering repay fees.
	 * @param _context Additional data to be passed to the call back.
	 * @return _success A boolean indicating whether or not the operation suceeded.
         */
	function _requestFlashLoan(address _token, uint256 _netAmount, bytes memory _context) internal returns (bool _success)
	{
		address _solo = $.Dydx_SOLO_MARGIN;
		uint256 _feeAmount = 2;
		uint256 _grossAmount = _netAmount.add(_feeAmount);
		// attempts to find the market id given a reserve token
		uint256 _marketId = uint256(-1);
		uint256 _numMarkets = SoloMargin(_solo).getNumMarkets();
		for (uint256 _i = 0; _i < _numMarkets; _i++) {
			address _address = SoloMargin(_solo).getMarketTokenAddress(_i);
			if (_address == _token) {
				_marketId = _i;
				break;
			}
		}
		if (_marketId == uint256(-1)) return false;
		// a flash loan on Dydx is achieved by the following sequence of
		// actions: withdrawal, user call back, and finally a deposit;
		// which is configured below
		Account.Info[] memory _accounts = new Account.Info[](1);
		_accounts[0] = Account.Info({ owner: address(this), number: 1 });
		Actions.ActionArgs[] memory _actions = new Actions.ActionArgs[](3);
		_actions[0] = Actions.ActionArgs({
			actionType: Actions.ActionType.Withdraw,
			accountId: 0,
			amount: Types.AssetAmount({
				sign: false,
				denomination: Types.AssetDenomination.Wei,
				ref: Types.AssetReference.Delta,
				value: _netAmount
			}),
			primaryMarketId: _marketId,
			secondaryMarketId: 0,
			otherAddress: address(this),
			otherAccountId: 0,
			data: ""
		});
		_actions[1] = Actions.ActionArgs({
			actionType: Actions.ActionType.Call,
			accountId: 0,
			amount: Types.AssetAmount({
				sign: false,
				denomination: Types.AssetDenomination.Wei,
				ref: Types.AssetReference.Delta,
				value: 0
			}),
			primaryMarketId: 0,
			secondaryMarketId: 0,
			otherAddress: address(this),
			otherAccountId: 0,
			data: abi.encode(_token, _netAmount, _feeAmount, _context)
		});
		_actions[2] = Actions.ActionArgs({
			actionType: Actions.ActionType.Deposit,
			accountId: 0,
			amount: Types.AssetAmount({
				sign: true,
				denomination: Types.AssetDenomination.Wei,
				ref: Types.AssetReference.Delta,
				value: _grossAmount
			}),
			primaryMarketId: _marketId,
			secondaryMarketId: 0,
			otherAddress: address(this),
			otherAccountId: 0,
			data: ""
		});
		try SoloMargin(_solo).operate(_accounts, _actions) {
			return true;
		} catch (bytes memory /* _data */) {
			return false;
		}
	}

	/**
	 * @dev This function should be called as the final step of the flash
	 *      loan to properly implement the repay of the loan.
	 * @param _token The reserve token.
	 * @param _grossAmount The amount to be repayed including repay fees.
	 */
	function _paybackFlashLoan(address _token, uint256 _grossAmount) internal
	{
		address _solo = $.Dydx_SOLO_MARGIN;
		Transfers._approveFunds(_token, _solo, _grossAmount);
	}
}

// File: contracts/modules/FlashLoans.sol

pragma solidity ^0.6.0;





/**
 * @dev This library abstracts the flash loan request combining both Aave/Dydx.
 *      See GFlashBorrower.sol, AaveFlashLoanAbstraction.sol, and
 *      DydxFlashLoanAbstraction.sol for further documentation.
 */
library FlashLoans
{
	enum Provider { Aave, Dydx }

	/**
	 * @dev Estimates the flash loan fee given the reserve token and required amount.
	 * @param _provider The flash loan provider, either Aave or Dydx.
	 * @param _token The ERC-20 token to flash borrow from.
	 * @param _netAmount The amount to be borrowed without considering repay fees.
	 * @param _feeAmount the expected fee to be payed in excees of the loan amount.
	 */
	function _estimateFlashLoanFee(Provider _provider, address _token, uint256 _netAmount) internal pure returns (uint256 _feeAmount)
	{
		if (_provider == Provider.Aave) return AaveFlashLoanAbstraction._estimateFlashLoanFee(_token, _netAmount);
		if (_provider == Provider.Dydx) return DydxFlashLoanAbstraction._estimateFlashLoanFee(_token, _netAmount);
	}

	/**
	 * @dev Retrieves the maximum market liquidity for a given reserve on
	 *      both Aave and Dydx.
	 * @param _token The reserve token to flash borrow from.
	 * @return _liquidityAmount The reserve token available market liquidity.
	 */
	function _getFlashLoanLiquidity(address _token) internal view returns (uint256 _liquidityAmount)
	{
		uint256 _liquidityAmountDydx = 0;
		_liquidityAmountDydx = DydxFlashLoanAbstraction._getFlashLoanLiquidity(_token);
		uint256 _liquidityAmountAave = 0;
		_liquidityAmountAave = AaveFlashLoanAbstraction._getFlashLoanLiquidity(_token);
		return Math._max(_liquidityAmountDydx, _liquidityAmountAave);
	}

	/**
	 * @dev Triggers a flash loan on Dydx and, if unsuccessful, on Aave.
	 *      The current contract will receive a call back with the loan
	 *      amount and should repay it, including fees, before returning.
	 *      See GFlashBorrow.sol.
	 * @param _token The reserve token to flash borrow from.
	 * @param _netAmount The amount to be borrowed without considering repay fees.
	 * @param _context Additional data to be passed to the call back.
	 * @return _success A boolean indicating whether or not the operation suceeded.
         */
	function _requestFlashLoan(address _token, uint256 _netAmount, bytes memory _context) internal returns (bool _success)
	{
		_success = DydxFlashLoanAbstraction._requestFlashLoan(_token, _netAmount, _context);
		if (_success) return true;
		_success = AaveFlashLoanAbstraction._requestFlashLoan(_token, _netAmount, _context);
		if (_success) return true;
		return false;
	}

	/**
	 * @dev This function should be called as the final step of the flash
	 *      loan to properly implement the repay of the loan.
	 * @param _provider The flash loan provider, either Aave or Dydx.
	 * @param _token The reserve token.
	 * @param _grossAmount The amount to be repayed including repay fees.
	 */
	function _paybackFlashLoan(Provider _provider, address _token, uint256 _grossAmount) internal
	{
		if (_provider == Provider.Aave) return AaveFlashLoanAbstraction._paybackFlashLoan(_token, _grossAmount);
		if (_provider == Provider.Dydx) return DydxFlashLoanAbstraction._paybackFlashLoan(_token, _grossAmount);
	}
}

// File: contracts/interop/Balancer.sol

pragma solidity ^0.6.0;


/**
 * @dev Minimal set of declarations for Balancer interoperability.
 */
interface BFactory
{
	function newBPool() external returns (address _pool);
}

interface BPool is IERC20
{
	function getFinalTokens() external view returns (address[] memory _tokens);
	function getBalance(address _token) external view returns (uint256 _balance);
	function setSwapFee(uint256 _swapFee) external;
	function finalize() external;
	function bind(address _token, uint256 _balance, uint256 _denorm) external;
	function exitPool(uint256 _poolAmountIn, uint256[] calldata _minAmountsOut) external;
	function joinswapExternAmountIn(address _tokenIn, uint256 _tokenAmountIn, uint256 _minPoolAmountOut) external returns (uint256 _poolAmountOut);
}

// File: contracts/modules/BalancerLiquidityPoolAbstraction.sol

pragma solidity ^0.6.0;






/**
 * @dev This library abstracts the Balancer liquidity pool operations.
 */
library BalancerLiquidityPoolAbstraction
{
	using SafeMath for uint256;

	uint256 constant MIN_AMOUNT = 1e6; // transported from Balancer
	uint256 constant TOKEN0_WEIGHT = 25e18; // 25/50 = 50%
	uint256 constant TOKEN1_WEIGHT = 25e18; // 25/50 = 50%
	uint256 constant SWAP_FEE = 10e16; // 10%

	/**
	 * @dev Creates a two-asset liquidity pool and funds it by depositing
	 *      both assets. The create pool is public with a 50%/50%
	 *      distribution and 10% swap fee.
	 * @param _token0 The ERC-20 token for the first asset of the pair.
	 * @param _amount0 The amount of the first asset of the pair to be deposited.
	 * @param _token1 The ERC-20 token for the second asset of the pair.
	 * @param _amount1 The amount of the second asset of the pair to be deposited.
	 * @return _pool The address of the newly created pool.
	 */
	function _createPool(address _token0, uint256 _amount0, address _token1, uint256 _amount1) internal returns (address _pool)
	{
		require(_amount0 >= MIN_AMOUNT && _amount1 >= MIN_AMOUNT, "amount below the minimum");
		_pool = BFactory($.Balancer_FACTORY).newBPool();
		Transfers._approveFunds(_token0, _pool, _amount0);
		Transfers._approveFunds(_token1, _pool, _amount1);
		BPool(_pool).bind(_token0, _amount0, TOKEN0_WEIGHT);
		BPool(_pool).bind(_token1, _amount1, TOKEN1_WEIGHT);
		BPool(_pool).setSwapFee(SWAP_FEE);
		BPool(_pool).finalize();
		return _pool;
	}

	/**
	 * @dev Deposits a single asset into the liquidity pool.
	 * @param _pool The liquidity pool address.
	 * @param _token The ERC-20 token for the asset being deposited.
	 * @param _maxAmount The maximum amount to be deposited.
	 * @return _amount The actual amount deposited.
	 */
	function _joinPool(address _pool, address _token, uint256 _maxAmount) internal returns (uint256 _amount)
	{
		if (_maxAmount == 0) return 0;
		uint256 _balanceAmount = BPool(_pool).getBalance(_token);
		if (_balanceAmount == 0) return 0;
		// caps the deposit amount to half the liquidity to mitigate error
		uint256 _limitAmount = _balanceAmount.div(2);
		_amount = Math._min(_maxAmount, _limitAmount);
		Transfers._approveFunds(_token, _pool, _amount);
		BPool(_pool).joinswapExternAmountIn(_token, _amount, 0);
		return _amount;
	}

	/**
	 * @dev Withdraws a percentage of the pool shares.
	 * @param _pool The liquidity pool address.
	 * @param _percent The percent amount normalized to 1e18 (100%).
	 * @return _amount0 The amount received of the first asset of the pair.
	 * @return _amount1 The amount received of the second asset of the pair.
	 */
	function _exitPool(address _pool, uint256 _percent) internal returns (uint256 _amount0, uint256 _amount1)
	{
		if (_percent == 0) return (0, 0);
		address[] memory _tokens = BPool(_pool).getFinalTokens();
		_amount0 = Transfers._getBalance(_tokens[0]);
		_amount1 = Transfers._getBalance(_tokens[1]);
		uint256 _poolAmount = Transfers._getBalance(_pool);
		uint256 _poolExitAmount = _poolAmount.mul(_percent).div(1e18);
		uint256[] memory _minAmountsOut = new uint256[](2);
		_minAmountsOut[0] = 0;
		_minAmountsOut[1] = 0;
		BPool(_pool).exitPool(_poolExitAmount, _minAmountsOut);
		_amount0 = Transfers._getBalance(_tokens[0]).sub(_amount0);
		_amount1 = Transfers._getBalance(_tokens[1]).sub(_amount1);
		return (_amount0, _amount1);
	}
}

// File: contracts/G.sol

pragma solidity ^0.6.0;







/**
 * @dev This public library provides a single entrypoint to most of the relevant
 *      internal libraries available in the modules folder. It exists to
 *      circunvent the contract size limitation imposed by the EVM. All function
 *      calls are directly delegated to the target library function preserving
 *      argument and return values exactly as they are. This library is shared
 *      by many contracts and even other public libraries from this repository,
 *      therefore it needs to be published alongside them.
 */
library G
{
	function min(uint256 _amount1, uint256 _amount2) public pure returns (uint256 _minAmount) { return Math._min(_amount1, _amount2); }

	function safeWrap(uint256 _amount) public { Wrapping._safeWrap(_amount); }
	function safeUnwrap(uint256 _amount) public { Wrapping._safeUnwrap(_amount); }

	function getBalance(address _token) public view returns (uint256 _balance) { return Transfers._getBalance(_token); }
	function pullFunds(address _token, address _from, uint256 _amount) public { Transfers._pullFunds(_token, _from, _amount); }
	function pushFunds(address _token, address _to, uint256 _amount) public { Transfers._pushFunds(_token, _to, _amount); }
	function approveFunds(address _token, address _to, uint256 _amount) public { Transfers._approveFunds(_token, _to, _amount); }

	function dynamicConvertFunds(address _exchange, address _from, address _to, uint256 _inputAmount, uint256 _minOutputAmount) public returns (uint256 _outputAmount) { return Conversions._dynamicConvertFunds(_exchange, _from, _to, _inputAmount, _minOutputAmount); }

	function getFlashLoanLiquidity(address _token) public view returns (uint256 _liquidityAmount) { return FlashLoans._getFlashLoanLiquidity(_token); }
	function requestFlashLoan(address _token, uint256 _amount, bytes memory _context) public returns (bool _success) { return FlashLoans._requestFlashLoan(_token, _amount, _context); }
	function paybackFlashLoan(FlashLoans.Provider _provider, address _token, uint256 _grossAmount) public { FlashLoans._paybackFlashLoan(_provider, _token, _grossAmount); }

	function createPool(address _token0, uint256 _amount0, address _token1, uint256 _amount1) public returns (address _pool) { return BalancerLiquidityPoolAbstraction._createPool(_token0, _amount0, _token1, _amount1); }
	function joinPool(address _pool, address _token, uint256 _maxAmount) public returns (uint256 _amount) { return BalancerLiquidityPoolAbstraction._joinPool(_pool, _token, _maxAmount); }
	function exitPool(address _pool, uint256 _percent) public returns (uint256 _amount0, uint256 _amount1) { return BalancerLiquidityPoolAbstraction._exitPool(_pool, _percent); }
}

// File: contracts/GLiquidityPoolManager.sol

pragma solidity ^0.6.0;


/**
 * @dev This library implements data structure abstraction for the liquidity
 *      pool management code in order to circuvent the EVM contract size limit.
 *      It is therefore a public library shared by all gToken contracts and
 *      needs to be published alongside them. See GTokenBase.sol for further
 *      documentation.
 */
library GLiquidityPoolManager
{
	using GLiquidityPoolManager for GLiquidityPoolManager.Self;

	uint256 constant MAXIMUM_BURNING_RATE = 2e16; // 2%
	uint256 constant DEFAULT_BURNING_RATE = 5e15; // 0.5%
	uint256 constant BURNING_INTERVAL = 7 days;
	uint256 constant MIGRATION_INTERVAL = 7 days;

	enum State { Created, Allocated, Migrating, Migrated }

	struct Self {
		address stakesToken;
		address sharesToken;

		State state;
		address liquidityPool;

		uint256 burningRate;
		uint256 lastBurningTime;

		address migrationRecipient;
		uint256 migrationUnlockTime;
	}

	/**
	 * @dev Initializes the data structure. This method is exposed publicly.
	 * @param _stakesToken The ERC-20 token address to be used as stakes
	 *                     token (GRO).
	 * @param _sharesToken The ERC-20 token address to be used as shares
	 *                     token (gToken).
	 */
	function init(Self storage _self, address _stakesToken, address _sharesToken) public
	{
		_self.stakesToken = _stakesToken;
		_self.sharesToken = _sharesToken;

		_self.state = State.Created;
		_self.liquidityPool = address(0);

		_self.burningRate = DEFAULT_BURNING_RATE;
		_self.lastBurningTime = 0;

		_self.migrationRecipient = address(0);
		_self.migrationUnlockTime = uint256(-1);
	}

	/**
	 * @dev Verifies whether or not a liquidity pool is migrating or
	 *      has migrated. This method is exposed publicly.
	 * @return _hasMigrated A boolean indicating whether or not the pool
	 *                      migration has started.
	 */
	function hasMigrated(Self storage _self) public view returns (bool _hasMigrated)
	{
		return _self.state == State.Migrating || _self.state == State.Migrated;
	}

	/**
	 * @dev Moves the current balances (if any) of stakes and shares tokens
	 *      to the liquidity pool. This method is exposed publicly.
	 */
	function gulpPoolAssets(Self storage _self) public
	{
		if (!_self._hasPool()) return;
		G.joinPool(_self.liquidityPool, _self.stakesToken, G.getBalance(_self.stakesToken));
		G.joinPool(_self.liquidityPool, _self.sharesToken, G.getBalance(_self.sharesToken));
	}

	/**
	 * @dev Sets the liquidity pool burning rate. This method is exposed
	 *      publicly.
	 * @param _burningRate The percent value of the liquidity pool to be
	 *                     burned at each 7-day period.
	 */
	function setBurningRate(Self storage _self, uint256 _burningRate) public
	{
		require(_burningRate <= MAXIMUM_BURNING_RATE, "invalid rate");
		_self.burningRate = _burningRate;
	}

	/**
	 * @dev Burns a portion of the liquidity pool according to the defined
	 *      burning rate. It must happen at most once every 7-days. This
	 *      method does not actually burn the funds, but it will redeem
	 *      the amounts from the pool to the caller contract, which is then
	 *      assumed to perform the burn. This method is exposed publicly.
	 * @return _stakesAmount The amount of stakes (GRO) redeemed from the pool.
	 * @return _sharesAmount The amount of shares (gToken) redeemed from the pool.
	 */
	function burnPoolPortion(Self storage _self) public returns (uint256 _stakesAmount, uint256 _sharesAmount)
	{
		require(_self._hasPool(), "pool not available");
		require(now >= _self.lastBurningTime + BURNING_INTERVAL, "must wait lock interval");
		_self.lastBurningTime = now;
		return G.exitPool(_self.liquidityPool, _self.burningRate);
	}

	/**
	 * @dev Creates a fresh new liquidity pool and deposits the initial
	 *      amounts of the stakes token and the shares token. The pool
	 *      if configure 50%/50% with a 10% swap fee. This method is exposed
	 *      publicly.
	 * @param _stakesAmount The amount of stakes token initially deposited
	 *                      into the pool.
	 * @param _sharesAmount The amount of shares token initially deposited
	 *                      into the pool.
	 */
	function allocatePool(Self storage _self, uint256 _stakesAmount, uint256 _sharesAmount) public
	{
		require(_self.state == State.Created, "pool cannot be allocated");
		_self.state = State.Allocated;
		_self.liquidityPool = G.createPool(_self.stakesToken, _stakesAmount, _self.sharesToken, _sharesAmount);
	}

	/**
	 * @dev Initiates the liquidity pool migration by setting a funds
	 *      recipent and starting the clock towards the 7-day grace period.
	 *      This method is exposed publicly.
	 * @param _migrationRecipient The recipient address to where funds will
	 *                            be transfered.
	 */
	function initiatePoolMigration(Self storage _self, address _migrationRecipient) public
	{
		require(_self.state == State.Allocated || _self.state == State.Migrated, "migration unavailable");
		_self.state = State.Migrating;
		_self.migrationRecipient = _migrationRecipient;
		_self.migrationUnlockTime = now + MIGRATION_INTERVAL;
	}

	/**
	 * @dev Cancels the liquidity pool migration by reseting the procedure
	 *      to its original state. This method is exposed publicly.
	 * @return _migrationRecipient The address of the former recipient.
	 */
	function cancelPoolMigration(Self storage _self) public returns (address _migrationRecipient)
	{
		require(_self.state == State.Migrating, "migration not initiated");
		_migrationRecipient = _self.migrationRecipient;
		_self.state = State.Allocated;
		_self.migrationRecipient = address(0);
		_self.migrationUnlockTime = uint256(-1);
		return _migrationRecipient;
	}

	/**
	 * @dev Completes the liquidity pool migration by redeeming all funds
	 *      from the pool. This method does not actually transfer the
	 *      redemeed funds to the recipient, it assumes the caller contract
	 *      will perform that. This method is exposed publicly.
	 * @return _migrationRecipient The address of the recipient.
	 * @return _stakesAmount The amount of stakes (GRO) redeemed from the pool.
	 * @return _sharesAmount The amount of shares (gToken) redeemed from the pool.
	 */
	function completePoolMigration(Self storage _self) public returns (address _migrationRecipient, uint256 _stakesAmount, uint256 _sharesAmount)
	{
		require(_self.state == State.Migrating, "migration not initiated");
		require(now >= _self.migrationUnlockTime, "must wait lock interval");
		_migrationRecipient = _self.migrationRecipient;
		_self.state = State.Migrated;
		_self.migrationRecipient = address(0);
		_self.migrationUnlockTime = uint256(-1);
		(_stakesAmount, _sharesAmount) = G.exitPool(_self.liquidityPool, 1e18);
		return (_migrationRecipient, _stakesAmount, _sharesAmount);
	}

	/**
	 * @dev Verifies whether or not a liquidity pool has been allocated.
	 * @return _poolAvailable A boolean indicating whether or not the pool
	 *                        is available.
	 */
	function _hasPool(Self storage _self) internal view returns (bool _poolAvailable)
	{
		return _self.state != State.Created;
	}
}

// File: contracts/GTokenBase.sol

pragma solidity ^0.6.0;









/**
 * @notice This abstract contract provides the basis implementation for all
 *         gTokens. It extends the ERC20 functionality by implementing all
 *         the methods of the GToken interface. The gToken basic functionality
 *         comprises of a reserve, provided in the reserve token, and a supply
 *         of shares. Every time someone deposits into the contract some amount
 *         of reserve tokens it will receive a given amount of this gToken
 *         shares. Conversely, upon withdrawal, someone redeems their previously
 *         deposited assets by providing the associated amount of gToken shares.
 *         The nominal price of a gToken is given by the ratio between the
 *         reserve balance and the total supply of shares. Upon deposit and
 *         withdrawal of funds a 1% fee is applied and collected from shares.
 *         Half of it is immediately burned, which is equivalent to
 *         redistributing it to all gToken holders, and the other half is
 *         provided to a liquidity pool configured as a 50% GRO/50% gToken with
 *         a 10% swap fee. Every week a percentage of the liquidity pool is
 *         burned to account for the accumulated swap fees for that period.
 *         Finally, the gToken contract provides functionality to migrate the
 *         total amount of funds locked in the liquidity pool to an external
 *         address, this mechanism is provided to facilitate the upgrade of
 *         this gToken contract by future implementations. After migration has
 *         started the fee for deposits becomes 2% and the fee for withdrawals
 *         becomes 0%, in order to incentivise others to follow the migration.
 */
abstract contract GTokenBase is ERC20, Ownable, ReentrancyGuard, GToken, GPooler
{
	using GLiquidityPoolManager for GLiquidityPoolManager.Self;

	uint256 constant DEPOSIT_FEE = 1e16; // 1%
	uint256 constant WITHDRAWAL_FEE = 1e16; // 1%
	uint256 constant DEPOSIT_FEE_AFTER_MIGRATION = 2e16; // 2%
	uint256 constant WITHDRAWAL_FEE_AFTER_MIGRATION = 0e16; // 0%

	address public immutable override stakesToken;
	address public immutable override reserveToken;

	GLiquidityPoolManager.Self lpm;

	/**
	 * @dev Constructor for the gToken contract.
	 * @param _name The ERC-20 token name.
	 * @param _symbol The ERC-20 token symbol.
	 * @param _decimals The ERC-20 token decimals.
	 * @param _stakesToken The ERC-20 token address to be used as stakes
	 *                     token (GRO).
	 * @param _reserveToken The ERC-20 token address to be used as reserve
	 *                      token (e.g. cDAI for gcDAI).
	 */
	constructor (string memory _name, string memory _symbol, uint8 _decimals, address _stakesToken, address _reserveToken)
		ERC20(_name, _symbol) public
	{
		_setupDecimals(_decimals);
		stakesToken = _stakesToken;
		reserveToken = _reserveToken;
		lpm.init(_stakesToken, address(this));
	}

	/**
	 * @notice Allows for the beforehand calculation of shares to be
	 *         received/minted upon depositing to the contract.
	 * @param _cost The amount of reserve token being deposited.
	 * @param _totalReserve The reserve balance as obtained by totalReserve().
	 * @param _totalSupply The shares supply as obtained by totalSupply().
	 * @param _depositFee The current deposit fee as obtained by depositFee().
	 * @return _netShares The net amount of shares being received.
	 * @return _feeShares The fee amount of shares being deducted.
	 */
	function calcDepositSharesFromCost(uint256 _cost, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee) public pure override returns (uint256 _netShares, uint256 _feeShares)
	{
		return GFormulae._calcDepositSharesFromCost(_cost, _totalReserve, _totalSupply, _depositFee);
	}

	/**
	 * @notice Allows for the beforehand calculation of the amount of
	 *         reserve token to be deposited in order to receive the desired
	 *         amount of shares.
	 * @param _netShares The amount of this gToken shares to receive.
	 * @param _totalReserve The reserve balance as obtained by totalReserve().
	 * @param _totalSupply The shares supply as obtained by totalSupply().
	 * @param _depositFee The current deposit fee as obtained by depositFee().
	 * @return _cost The cost, in the reserve token, to be paid.
	 * @return _feeShares The fee amount of shares being deducted.
	 */
	function calcDepositCostFromShares(uint256 _netShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee) public pure override returns (uint256 _cost, uint256 _feeShares)
	{
		return GFormulae._calcDepositCostFromShares(_netShares, _totalReserve, _totalSupply, _depositFee);
	}

	/**
	 * @notice Allows for the beforehand calculation of shares to be
	 *         given/burned upon withdrawing from the contract.
	 * @param _cost The amount of reserve token being withdrawn.
	 * @param _totalReserve The reserve balance as obtained by totalReserve()
	 * @param _totalSupply The shares supply as obtained by totalSupply()
	 * @param _withdrawalFee The current withdrawal fee as obtained by withdrawalFee()
	 * @return _grossShares The total amount of shares being deducted,
	 *                      including fees.
	 * @return _feeShares The fee amount of shares being deducted.
	 */
	function calcWithdrawalSharesFromCost(uint256 _cost, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee) public pure override returns (uint256 _grossShares, uint256 _feeShares)
	{
		return GFormulae._calcWithdrawalSharesFromCost(_cost, _totalReserve, _totalSupply, _withdrawalFee);
	}

	/**
	 * @notice Allows for the beforehand calculation of the amount of
	 *         reserve token to be withdrawn given the desired amount of
	 *         shares.
	 * @param _grossShares The amount of this gToken shares to provide.
	 * @param _totalReserve The reserve balance as obtained by totalReserve().
	 * @param _totalSupply The shares supply as obtained by totalSupply().
	 * @param _withdrawalFee The current withdrawal fee as obtained by withdrawalFee().
	 * @return _cost The cost, in the reserve token, to be received.
	 * @return _feeShares The fee amount of shares being deducted.
	 */
	function calcWithdrawalCostFromShares(uint256 _grossShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee) public pure override returns (uint256 _cost, uint256 _feeShares)
	{
		return GFormulae._calcWithdrawalCostFromShares(_grossShares, _totalReserve, _totalSupply, _withdrawalFee);
	}

	/**
	 * @notice Provides the amount of reserve tokens currently being help by
	 *         this contract.
	 * @return _totalReserve The amount of the reserve token corresponding
	 *                       to this contract's balance.
	 */
	function totalReserve() public view virtual override returns (uint256 _totalReserve)
	{
		return G.getBalance(reserveToken);
	}

	/**
	 * @notice Provides the current minting/deposit fee. This fee is
	 *         applied to the amount of this gToken shares being created
	 *         upon deposit. The fee defaults to 1% and is set to 2%
	 *         after the liquidity pool has been migrated.
	 * @return _depositFee A percent value that accounts for the percentage
	 *                     of shares being minted at each deposit that be
	 *                     collected as fee.
	 */
	function depositFee() public view override returns (uint256 _depositFee) {
		return lpm.hasMigrated() ? DEPOSIT_FEE_AFTER_MIGRATION : DEPOSIT_FEE;
	}

	/**
	 * @notice Provides the current burning/withdrawal fee. This fee is
	 *         applied to the amount of this gToken shares being redeemed
	 *         upon withdrawal. The fee defaults to 1% and is set to 0%
	 *         after the liquidity pool is migrated.
	 * @return _withdrawalFee A percent value that accounts for the
	 *                        percentage of shares being burned at each
	 *                        withdrawal that be collected as fee.
	 */
	function withdrawalFee() public view override returns (uint256 _withdrawalFee) {
		return lpm.hasMigrated() ? WITHDRAWAL_FEE_AFTER_MIGRATION : WITHDRAWAL_FEE;
	}

	/**
	 * @notice Provides the address of the liquidity pool contract.
	 * @return _liquidityPool An address identifying the liquidity pool.
	 */
	function liquidityPool() public view override returns (address _liquidityPool)
	{
		return lpm.liquidityPool;
	}

	/**
	 * @notice Provides the percentage of the liquidity pool to be burned.
	 *         This amount should account approximately for the swap fees
	 *         collected by the liquidity pool during a 7-day period.
	 * @return _burningRate A percent value that corresponds to the current
	 *                      amount of the liquidity pool to be burned at
	 *                      each 7-day cycle.
	 */
	function liquidityPoolBurningRate() public view override returns (uint256 _burningRate)
	{
		return lpm.burningRate;
	}

	/**
	 * @notice Marks when the last liquidity pool burn took place. There is
	 *         a minimum 7-day grace period between consecutive burnings of
	 *         the liquidity pool.
	 * @return _lastBurningTime A timestamp for when the liquidity pool
	 *                          burning took place for the last time.
	 */
	function liquidityPoolLastBurningTime() public view override returns (uint256 _lastBurningTime)
	{
		return lpm.lastBurningTime;
	}

	/**
	 * @notice Provides the address receiving the liquidity pool migration.
	 * @return _migrationRecipient An address to which funds will be sent
	 *                             upon liquidity pool migration completion.
	 */
	function liquidityPoolMigrationRecipient() public view override returns (address _migrationRecipient)
	{
		return lpm.migrationRecipient;
	}

	/**
	 * @notice Provides the timestamp for when the liquidity pool migration
	 *         can be completed.
	 * @return _migrationUnlockTime A timestamp that defines the end of the
	 *                              7-day grace period for liquidity pool
	 *                              migration.
	 */
	function liquidityPoolMigrationUnlockTime() public view override returns (uint256 _migrationUnlockTime)
	{
		return lpm.migrationUnlockTime;
	}

	/**
	 * @notice Performs the minting of gToken shares upon the deposit of the
	 *         reserve token. The actual number of shares being minted can
	 *         be calculated using the calcDepositSharesFromCost function.
	 *         In every deposit, 1% of the shares is retained in terms of
	 *         deposit fee. Half of it is immediately burned and the other
	 *         half is provided to the locked liquidity pool. The funds
	 *         will be pulled in by this contract, therefore they must be
	 *         previously approved.
	 * @param _cost The amount of reserve token being deposited in the
	 *              operation.
	 */
	function deposit(uint256 _cost) public override nonReentrant
	{
		address _from = msg.sender;
		require(_cost > 0, "cost must be greater than 0");
		(uint256 _netShares, uint256 _feeShares) = GFormulae._calcDepositSharesFromCost(_cost, totalReserve(), totalSupply(), depositFee());
		require(_netShares > 0, "shares must be greater than 0");
		G.pullFunds(reserveToken, _from, _cost);
		require(_prepareDeposit(_cost), "not available at the moment");
		_mint(_from, _netShares);
		_mint(address(this), _feeShares.div(2));
	}

	/**
	 * @notice Performs the burning of gToken shares upon the withdrawal of
	 *         the reserve token. The actual amount of the reserve token to
	 *         be received can be calculated using the
	 *         calcWithdrawalCostFromShares function. In every withdrawal,
	 *         1% of the shares is retained in terms of withdrawal fee.
	 *         Half of it is immediately burned and the other half is
	 *         provided to the locked liquidity pool.
	 * @param _grossShares The gross amount of this gToken shares being
	 *                     redeemed in the operation.
	 */
	function withdraw(uint256 _grossShares) public override nonReentrant
	{
		address _from = msg.sender;
		require(_grossShares > 0, "shares must be greater than 0");
		(uint256 _cost, uint256 _feeShares) = GFormulae._calcWithdrawalCostFromShares(_grossShares, totalReserve(), totalSupply(), withdrawalFee());
		require(_cost > 0, "cost must be greater than 0");
		require(_prepareWithdrawal(_cost), "not available at the moment");
		_cost = G.min(_cost, G.getBalance(reserveToken));
		G.pushFunds(reserveToken, _from, _cost);
		_burn(_from, _grossShares);
		_mint(address(this), _feeShares.div(2));
	}

	/**
	 * @notice Allocates a liquidity pool with the given amount of funds and
	 *         locks it to this contract. This function should be called
	 *         shortly after the contract is created to associated a newly
	 *         created liquidity pool to it, which will collect fees
	 *         associated with the minting and burning of this gToken shares.
	 *         The liquidity pool will consist of a 50%/50% balance of the
	 *         stakes token (GRO) and this gToken shares with a swap fee of
	 *         10%. The rate between the amount of the two assets deposited
	 *         via this function defines the initial price. The minimum
	 *         amount to be provided for each is 1,000,000 wei. The funds
	 *         will be pulled in by this contract, therefore they must be
	 *         previously approved. This is a priviledged function
	 *         restricted to the contract owner.
	 * @param _stakesAmount The initial amount of stakes token.
	 * @param _sharesAmount The initial amount of this gToken shares.
	 */
	function allocateLiquidityPool(uint256 _stakesAmount, uint256 _sharesAmount) public override onlyOwner nonReentrant
	{
		address _from = msg.sender;
		G.pullFunds(stakesToken, _from, _stakesAmount);
		_transfer(_from, address(this), _sharesAmount);
		lpm.allocatePool(_stakesAmount, _sharesAmount);
	}

	/**
	 * @notice Changes the percentual amount of the funds to be burned from
	 *         the liquidity pool at each 7-day period. This is a
	 *         priviledged function restricted to the contract owner.
	 * @param _burningRate The percentage of the liquidity pool to be burned.
	 */
	function setLiquidityPoolBurningRate(uint256 _burningRate) public override onlyOwner nonReentrant
	{
		lpm.setBurningRate(_burningRate);
	}

	/**
	 * @notice Burns part of the liquidity pool funds decreasing the supply
	 *         of both the stakes token and this gToken shares.
	 *         The amount to be burned is set via the function
	 *         setLiquidityPoolBurningRate and defaults to 0.5%.
	 *         After this function is called there must be a 7-day wait
	 *         period before it can be called again.
	 *         The purpose of this function is to burn the aproximate amount
	 *         of fees collected from swaps that take place in the liquidity
	 *         pool during the previous 7-day period. This function will
	 *         emit a BurnLiquidityPoolPortion event upon success. This is
	 *         a priviledged function restricted to the contract owner.
	 */
	function burnLiquidityPoolPortion() public override onlyOwner nonReentrant
	{
		lpm.gulpPoolAssets();
		(uint256 _stakesAmount, uint256 _sharesAmount) = lpm.burnPoolPortion();
		_burnStakes(_stakesAmount);
		_burn(address(this), _sharesAmount);
		emit BurnLiquidityPoolPortion(_stakesAmount, _sharesAmount);
	}

	/**
	 * @notice Initiates the liquidity pool migration. It consists of
	 *         setting the migration recipient address and starting a
	 *         7-day grace period. After the 7-day grace period the
	 *         migration can be completed via the
	 *         completeLiquidityPoolMigration fuction. Anytime before
	 *         the migration is completed is can be cancelled via
	 *         cancelLiquidityPoolMigration. This function will emit a
	 *         InitiateLiquidityPoolMigration event upon success. This is
	 *         a priviledged function restricted to the contract owner.
	 * @param _migrationRecipient The receiver of the liquidity pool funds.
	 */
	function initiateLiquidityPoolMigration(address _migrationRecipient) public override onlyOwner nonReentrant
	{
		lpm.initiatePoolMigration(_migrationRecipient);
		emit InitiateLiquidityPoolMigration(_migrationRecipient);
	}

	/**
	 * @notice Cancels the liquidity pool migration if it has been already
	 *         initiated. This will reset the state of the liquidity pool
	 *         migration. This function will emit a
	 *         CancelLiquidityPoolMigration event upon success. This is
	 *         a priviledged function restricted to the contract owner.
	 */
	function cancelLiquidityPoolMigration() public override onlyOwner nonReentrant
	{
		address _migrationRecipient = lpm.cancelPoolMigration();
		emit CancelLiquidityPoolMigration(_migrationRecipient);
	}

	/**
	 * @notice Completes the liquidity pool migration at least 7-days after
	 *         it has been started. The migration consists of sendind the
	 *         the full balance held in the liquidity pool, both in the
	 *         stakes token and gToken shares, to the address set when
	 *         the migration was initiated. This function will emit a
	 *         CompleteLiquidityPoolMigration event upon success. This is
	 *         a priviledged function restricted to the contract owner.
	 */
	function completeLiquidityPoolMigration() public override onlyOwner nonReentrant
	{
		lpm.gulpPoolAssets();
		(address _migrationRecipient, uint256 _stakesAmount, uint256 _sharesAmount) = lpm.completePoolMigration();
		G.pushFunds(stakesToken, _migrationRecipient, _stakesAmount);
		_transfer(address(this), _migrationRecipient, _sharesAmount);
		emit CompleteLiquidityPoolMigration(_migrationRecipient, _stakesAmount, _sharesAmount);
	}

	/**
	 * @dev This abstract method must be implemented by subcontracts in
	 *      order to adjust the underlying reserve after a deposit takes
	 *      place. The actual implementation depends on the strategy and
	 *      algorithm used to handle the reserve.
	 * @param _cost The amount of the reserve token being deposited.
	 */
	function _prepareDeposit(uint256 _cost) internal virtual returns (bool _success);

	/**
	 * @dev This abstract method must be implemented by subcontracts in
	 *      order to adjust the underlying reserve before a withdrawal takes
	 *      place. The actual implementation depends on the strategy and
	 *      algorithm used to handle the reserve.
	 * @param _cost The amount of the reserve token being withdrawn.
	 */
	function _prepareWithdrawal(uint256 _cost) internal virtual returns (bool _success);

	/**
	 * @dev Burns the given amount of the stakes token. The default behavior
	 *      of the function for general ERC-20 is to send the funds to
	 *      address(0), but that can be overriden by a subcontract.
	 * @param _stakesAmount The amount of the stakes token being burned.
	 */
	function _burnStakes(uint256 _stakesAmount) internal virtual
	{
		G.pushFunds(stakesToken, address(0), _stakesAmount);
	}
}

// File: contracts/GPortfolio.sol

pragma solidity ^0.6.0;

/**
 * @dev An interface with the extended functionality of portfolio management
 *      gTokens. See GTokenType0.sol for further documentation.
 */
interface GPortfolio
{
	// view functions
	function tokenCount() external view returns (uint256 _count);
	function tokenAt(uint256 _index) external view returns (address _token);
	function tokenPercent(address _token) external view returns (uint256 _percent);
	function getRebalanceMargins() external view returns (uint256 _liquidRebalanceMargin, uint256 _portfolioRebalanceMargin);

	// priviledged functions
	function insertToken(address _token) external;
	function removeToken(address _token) external;
	function anounceTokenPercentTransfer(address _sourceToken, address _targetToken, uint256 _percent) external;
	function transferTokenPercent(address _sourceToken, address _targetToken, uint256 _percent) external;
	function setRebalanceMargins(uint256 _liquidRebalanceMargin, uint256 _portfolioRebalanceMargin) external;

	// emitted events
	event InsertToken(address indexed _token);
	event RemoveToken(address indexed _token);
	event AnnounceTokenPercentTransfer(address indexed _sourceToken, address indexed _targetToken, uint256 _percent);
	event TransferTokenPercent(address indexed _sourceToken, address indexed _targetToken, uint256 _percent);
	event ChangeTokenPercent(address indexed _token, uint256 _oldPercent, uint256 _newPercent);
}

// File: @openzeppelin/contracts/utils/EnumerableSet.sol


pragma solidity ^0.6.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
 * (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

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

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

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

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

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

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

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

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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


    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

// File: contracts/GCToken.sol

pragma solidity ^0.6.0;


/**
 * @dev Minimal interface for gcTokens, implemented by the GCTokenBase contract.
 *      See GCTokenBase.sol for further documentation.
 */
interface GCToken is GToken
{
	// pure functions
	function calcCostFromUnderlyingCost(uint256 _underlyingCost, uint256 _exchangeRate) external pure returns (uint256 _cost);
	function calcUnderlyingCostFromCost(uint256 _cost, uint256 _exchangeRate) external pure returns (uint256 _underlyingCost);
	function calcDepositSharesFromUnderlyingCost(uint256 _underlyingCost, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee, uint256 _exchangeRate) external pure returns (uint256 _netShares, uint256 _feeShares);
	function calcDepositUnderlyingCostFromShares(uint256 _netShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee, uint256 _exchangeRate) external pure returns (uint256 _underlyingCost, uint256 _feeShares);
	function calcWithdrawalSharesFromUnderlyingCost(uint256 _underlyingCost, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee, uint256 _exchangeRate) external pure returns (uint256 _grossShares, uint256 _feeShares);
	function calcWithdrawalUnderlyingCostFromShares(uint256 _grossShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee, uint256 _exchangeRate) external pure returns (uint256 _underlyingCost, uint256 _feeShares);

	// view functions
	function underlyingToken() external view returns (address _underlyingToken);
	function exchangeRate() external view returns (uint256 _exchangeRate);
	function totalReserveUnderlying() external view returns (uint256 _totalReserveUnderlying);
	function lendingReserveUnderlying() external view returns (uint256 _lendingReserveUnderlying);
	function borrowingReserveUnderlying() external view returns (uint256 _borrowingReserveUnderlying);
	function collateralizationRatio() external view returns (uint256 _collateralizationRatio, uint256 _collateralizationMargin);

	// open functions
	function depositUnderlying(uint256 _underlyingCost) external;
	function withdrawUnderlying(uint256 _grossShares) external;

	// priviledged functions
	function setCollateralizationRatio(uint256 _collateralizationRatio, uint256 _collateralizationMargin) external;
}

// File: contracts/GPortfolioReserveManager.sol

pragma solidity ^0.6.0;





/**
 * @dev This library implements data structure abstraction for the portfolio
 *      reserve management code in order to circuvent the EVM contract size limit.
 *      It is therefore a public library shared by all gToken Type 0 contracts and
 *      needs to be published alongside them. See GTokenType0.sol for further
 *      documentation.
 */
library GPortfolioReserveManager
{
	using SafeMath for uint256;
	using EnumerableSet for EnumerableSet.AddressSet;
	using GPortfolioReserveManager for GPortfolioReserveManager.Self;

	uint256 constant DEFAULT_LIQUID_REBALANCE_MARGIN = 95e15; // 9.5%
	uint256 constant DEFAULT_PORTFOLIO_REBALANCE_MARGIN = 1e16; // 1%
	uint256 constant MAXIMUM_TOKEN_COUNT = 5;
	uint256 constant PORTFOLIO_CHANGE_WAIT_INTERVAL = 1 days;
	uint256 constant PORTFOLIO_CHANGE_OPEN_INTERVAL = 1 days;

	struct Self {
		address reserveToken;
		EnumerableSet.AddressSet tokens;
		mapping (address => uint256) percents;
		mapping (uint256 => uint256) announcements;
		uint256 liquidRebalanceMargin;
		uint256 portfolioRebalanceMargin;
	}

	/**
	 * @dev Initializes the data structure. This method is exposed publicly.
	 * @param _reserveToken The ERC-20 token address of the reserve token.
	 */
	function init(Self storage _self, address _reserveToken) public
	{
		_self.reserveToken = _reserveToken;
		_self.percents[_reserveToken] = 1e18;
		_self.liquidRebalanceMargin = DEFAULT_LIQUID_REBALANCE_MARGIN;
		_self.portfolioRebalanceMargin = DEFAULT_PORTFOLIO_REBALANCE_MARGIN;
	}

	/**
	 * @dev The total number of gTokens added to the portfolio. This method
	 *      is exposed publicly.
	 * @return _count The number of gTokens that make up the portfolio.
	 */
	function tokenCount(Self storage _self) public view returns (uint256 _count)
	{
		return _self.tokens.length();
	}

	/**
	 * @dev Returns one of the gTokens that makes up the portfolio. This
	 *      method is exposed publicly.
	 * @param _index The desired index, must be less than the token count.
	 * @return _token The gToken currently present at the given index.
	 */
	function tokenAt(Self storage _self, uint256 _index) public view returns (address _token)
	{
		require(_index < _self.tokens.length(), "invalid index");
		return _self.tokens.at(_index);
	}

	/**
	 * @dev Returns the percentual participation of a token (including
	 *      the reserve token) in the portfolio composition. This method is
	 *      exposed publicly.
	 * @param _token The given token address.
	 * @return _percent The token percentual share of the portfolio.
	 */
	function tokenPercent(Self storage _self, address _token) public view returns (uint256 _percent)
	{
		return _self.percents[_token];
	}

	/**
	 * @dev Inserts a new gToken into the portfolio. The new gToken must
	 *      have the reserve token as its underlying token. The initial
	 *      portfolio share of the new token will be 0%. This method is
	 *      exposed publicly.
	 * @param _token The contract address of the new gToken to be incorporated
	 *               into the portfolio.
	 */
	function insertToken(Self storage _self, address _token) public
	{
		require(_self.tokens.length() < MAXIMUM_TOKEN_COUNT, "limit reached");
		address _underlyingToken = GCToken(_token).underlyingToken();
		require(_underlyingToken == _self.reserveToken, "mismatched token");
		require(_self.tokens.add(_token), "duplicate token");
		assert(_self.percents[_token] == 0);
	}

	/**
	 * @dev Removes a gToken from the portfolio. The portfolio share of the
	 *      token must be 0% before it can be removed. The underlying reserve
	 *      is redeemed upon removal. This method is exposed publicly.
	 * @param _token The contract address of the gToken to be removed from
	 *               the portfolio.
	 */
	function removeToken(Self storage _self, address _token) public
	{
		require(_self.percents[_token] == 0, "positive percent");
		require(_self.tokens.remove(_token), "unknown token");
		_self._withdrawUnderlying(_token, _self._getUnderlyingReserve(_token));
	}

	/**
	 * @dev Announces a token percent transfer before it can happen.
	 * @param _sourceToken The token address to provide the share.
	 * @param _targetToken The token address to receive the share.
	 * @param _percent The percentual share to shift.
	 */
	function announceTokenPercentTransfer(Self storage _self, address _sourceToken, address _targetToken, uint256 _percent) public
	{
		uint256 _hash = uint256(keccak256(abi.encode(uint256(_sourceToken), uint256(_targetToken), _percent)));
		uint256 _announcementTime = now;
		_self.announcements[_hash] = _announcementTime;
	}

	/**
	 * @dev Shifts a percentual share of the portfolio allocation from
	 *      one gToken to another gToken. The reserve token can also be
	 *      used as source or target of the operation. This does not
	 *      actually shifts funds, only reconfigures the allocation.
	 *      This method is exposed publicly. Note that in order to perform
	 *      a token transfer where the target token is not the reserve token
	 *      one must account the transfer ahead of time.
	 *      See anounceTokenPercentTransfer().
	 * @param _sourceToken The token address to provide the share.
	 * @param _targetToken The token address to receive the share.
	 * @param _percent The percentual share to shift.
	 */
	function transferTokenPercent(Self storage _self, address _sourceToken, address _targetToken, uint256 _percent) public
	{
		require(_percent <= _self.percents[_sourceToken], "invalid percent");
		require(_sourceToken != _targetToken, "invalid transfer");
		require(_targetToken == _self.reserveToken || _self.tokens.contains(_targetToken), "unknown token");
		uint256 _hash = uint256(keccak256(abi.encode(uint256(_sourceToken), uint256(_targetToken), _percent)));
		uint256 _announcementTime = _self.announcements[_hash];
		uint256 _effectiveTime = _announcementTime + PORTFOLIO_CHANGE_WAIT_INTERVAL;
		uint256 _cutoffTime = _effectiveTime + PORTFOLIO_CHANGE_OPEN_INTERVAL;
		require(_targetToken == _self.reserveToken || _effectiveTime <= now && now < _cutoffTime, "unannounced transfer");
		_self.announcements[_hash] = 0;
		_self.percents[_sourceToken] -= _percent;
		_self.percents[_targetToken] += _percent;
	}

	/**
	 * @dev Sets the percentual margins tolerable before triggering a
	 *      rebalance action (i.e. an underlying deposit or withdrawal).
	 *      This method is exposed publicly.
	 * @param _liquidRebalanceMargin The liquid percentual rebalance margin,
	 *                               to be configured by the owner.
	 * @param _portfolioRebalanceMargin The portfolio percentual rebalance
	 *                                  margin, to be configured by the owner.
	 */
	function setRebalanceMargins(Self storage _self, uint256 _liquidRebalanceMargin, uint256 _portfolioRebalanceMargin) public
	{
		require(0 <= _liquidRebalanceMargin && _liquidRebalanceMargin <= 1e18, "invalid margin");
		require(0 <= _portfolioRebalanceMargin && _portfolioRebalanceMargin <= 1e18, "invalid margin");
		_self.liquidRebalanceMargin = _liquidRebalanceMargin;
		_self.portfolioRebalanceMargin = _portfolioRebalanceMargin;
	}

	/**
	 * @dev Returns the total reserve amount held liquid by the contract
	 *      summed up with the underlying reserve of all gTokens that make up
	 *      the portfolio. This method is exposed publicly.
	 * @return _totalReserve The computed total reserve amount.
	 */
	function totalReserve(Self storage _self) public view returns (uint256 _totalReserve)
	{
		return _self._calcTotalReserve();
	}

	/**
	 * @dev Performs the reserve adjustment actions leaving a liquidity room,
	 *      if necessary. It will attempt to perform the operation using the
	 *      liquid pool and, if necessary, either withdrawal from an underlying
	 *      gToken to get more liquidity, or deposit/withdrawal from an
	 *      underlying gToken to move towards the desired reserve allocation
	 *      if any of them falls beyond the rebalance margin thresholds.
	 *      To save on gas costs the reserve adjusment will request at most
	 *      one operation from any of the underlying gTokens. This method is
	 *      exposed publicly.
	 * @param _roomAmount The underlying token amount to be available after the
	 *                    operation. This is revelant for withdrawals, once the
	 *                    room amount is withdrawn the reserve should reflect
	 *                    the configured collateralization ratio.
	 * @return _success A boolean indicating whether or not both actions suceeded.
	 */
	function adjustReserve(Self storage _self, uint256 _roomAmount) public returns (bool _success)
	{
		// the reserve amount must deduct the room requested
		uint256 _reserveAmount = _self._calcTotalReserve();
		_roomAmount = G.min(_roomAmount, _reserveAmount);
		_reserveAmount = _reserveAmount.sub(_roomAmount);

		// the liquid amount must deduct the room requested
		uint256 _liquidAmount = G.getBalance(_self.reserveToken);
		uint256 _blockedAmount = G.min(_roomAmount, _liquidAmount);
		_liquidAmount = _liquidAmount.sub(_blockedAmount);

		// calculates whether or not the liquid amount exceeds the
		// configured range and requires either a deposit or a withdrawal
		// to be performed
		(uint256 _depositAmount, uint256 _withdrawalAmount) = _self._calcLiquidAdjustment(_reserveAmount, _liquidAmount);

		// if the liquid amount is not enough to process a withdrawal
		// we will need to withdraw the missing amount from one of the
		// underlying gTokens (actually we will choose the one for which
		// the withdrawal will produce the least impact in terms of
		// percentual share deviation from its configured target)
		uint256 _requiredAmount = _roomAmount.sub(_blockedAmount);
		if (_requiredAmount > 0) {
			_withdrawalAmount = _withdrawalAmount.add(_requiredAmount);
			(address _adjustToken, uint256 _adjustAmount) = _self._findRequiredWithdrawal(_reserveAmount, _requiredAmount, _withdrawalAmount);
			if (_adjustToken == address(0)) return false;
			return _self._withdrawUnderlying(_adjustToken, _adjustAmount);
		}

		// finds the gToken that will have benefited more of this deposit
		// in terms of its target percentual share deviation and performs
		// the deposit on it
		if (_depositAmount > 0) {
			(address _adjustToken, uint256 _adjustAmount) = _self._findDeposit(_reserveAmount);
			if (_adjustToken == address(0)) return true;
			return _self._depositUnderlying(_adjustToken, G.min(_adjustAmount, _depositAmount));
		}

		// finds the gToken that will have benefited more of this withdrawal
		// in terms of its target percentual share deviation and performs
		// the withdrawal on it
		if (_withdrawalAmount > 0) {
			(address _adjustToken, uint256 _adjustAmount) = _self._findWithdrawal(_reserveAmount);
			if (_adjustToken == address(0)) return true;
			return _self._withdrawUnderlying(_adjustToken, G.min(_adjustAmount, _withdrawalAmount));
		}

		return true;
	}

	/**
	 * @dev Calculates the total reserve amount. It sums up the reserve held
	 *      by the contract with the underlying reserve held by the gTokens
	 *      that make up the portfolio.
	 * @return _totalReserve The computed total reserve amount.
	 */
	function _calcTotalReserve(Self storage _self) internal view returns (uint256 _totalReserve)
	{
		_totalReserve = G.getBalance(_self.reserveToken);
		uint256 _tokenCount = _self.tokens.length();
		for (uint256 _index = 0; _index < _tokenCount; _index++) {
			address _token = _self.tokens.at(_index);
			uint256 _tokenReserve = _self._getUnderlyingReserve(_token);
			_totalReserve = _totalReserve.add(_tokenReserve);
		}
		return _totalReserve;
	}

	/**
	 * @dev Calculates the amount that falls either above or below
	 *      the rebalance margin for the liquid pool. If we have more
	 *      liquid amount than its configured share plus the rebalance
	 *      margin it returns that amount paired with zero. If we have less
	 *      liquid amount than its configured share minus the rebalance
	 *      margin it returns zero paired with that amount. If none of these
	 *      two situations happen, then the liquid amount falls within the
	 *      acceptable parameters, and it returns a pair of zeros.
	 * @param _reserveAmount The total reserve amount used for calculation.
	 * @param _liquidAmount The liquid amount available used for calculation.
	 * @return _depositAmount The amount to be deposited or zero.
	 * @return _withdrawalAmount The amount to be withdrawn or zero.
	 */
	function _calcLiquidAdjustment(Self storage _self, uint256 _reserveAmount, uint256 _liquidAmount) internal view returns (uint256 _depositAmount, uint256 _withdrawalAmount)
	{
		uint256 _tokenPercent = _self.percents[_self.reserveToken];
		uint256 _tokenReserve = _reserveAmount.mul(_tokenPercent).div(1e18);
		if (_liquidAmount > _tokenReserve) {
			uint256 _upperPercent = G.min(1e18, _tokenPercent.add(_self.liquidRebalanceMargin));
			uint256 _upperReserve = _reserveAmount.mul(_upperPercent).div(1e18);
			if (_liquidAmount > _upperReserve) return (_liquidAmount.sub(_tokenReserve), 0);
		}
		else
		if (_liquidAmount < _tokenReserve) {
			uint256 _lowerPercent = _tokenPercent.sub(G.min(_tokenPercent, _self.liquidRebalanceMargin));
			uint256 _lowerReserve = _reserveAmount.mul(_lowerPercent).div(1e18);
			if (_liquidAmount < _lowerReserve) return (0, _tokenReserve.sub(_liquidAmount));
		}
		return (0, 0);
	}

	/**
	 * @dev Search the list of gTokens and selects the one that has enough
	 *      liquidity and for which the withdrawal of the required amount
	 *      will yield the least deviation from its target share.
	 * @param _reserveAmount The total reserve amount used for calculation.
	 * @param _minimumAmount The minimum liquidity amount used for calculation.
	 * @param _targetAmount The target liquidity amount used for calculation.
	 * @return _adjustToken The gToken to withdraw from.
	 * @return _adjustAmount The amount to be withdrawn.
	 */
	function _findRequiredWithdrawal(Self storage _self, uint256 _reserveAmount, uint256 _minimumAmount, uint256 _targetAmount) internal view returns (address _adjustToken, uint256 _adjustAmount)
	{
		uint256 _minPercent = 1e18;
		_adjustToken = address(0);
		_adjustAmount = 0;

		uint256 _tokenCount = _self.tokens.length();
		for (uint256 _index = 0; _index < _tokenCount; _index++) {
			address _token = _self.tokens.at(_index);
			uint256 _tokenReserve = _self._getUnderlyingReserve(_token);
			if (_tokenReserve < _minimumAmount) continue;
			uint256 _maximumAmount = G.min(_tokenReserve, _targetAmount);

			uint256 _oldTokenReserve = _tokenReserve.sub(_maximumAmount);
			uint256 _oldTokenPercent = _oldTokenReserve.mul(1e18).div(_reserveAmount);
			uint256 _newTokenPercent = _self.percents[_token];

			uint256 _percent = 0;
			if (_newTokenPercent > _oldTokenPercent) _percent = _newTokenPercent.sub(_oldTokenPercent);
			else
			if (_newTokenPercent < _oldTokenPercent) _percent = _oldTokenPercent.sub(_newTokenPercent);

			if (_maximumAmount > _adjustAmount || _maximumAmount == _adjustAmount && _percent < _minPercent) {
				_minPercent = _percent;
				_adjustToken = _token;
				_adjustAmount = _maximumAmount;
			}
		}

		return (_adjustToken, _adjustAmount);
	}

	/**
	 * @dev Search the list of gTokens and selects the one for which the
	 *      deposit will provide the best correction of deviation from
	 *      its target share.
	 * @param _reserveAmount The total reserve amount used for calculation.
	 * @return _adjustToken The gToken to deposit to.
	 * @return _adjustAmount The amount to be deposited.
	 */
	function _findDeposit(Self storage _self, uint256 _reserveAmount) internal view returns (address _adjustToken, uint256 _adjustAmount)
	{
		uint256 _maxPercent = _self.portfolioRebalanceMargin;
		_adjustToken = address(0);
		_adjustAmount = 0;

		uint256 _tokenCount = _self.tokens.length();
		for (uint256 _index = 0; _index < _tokenCount; _index++) {
			address _token = _self.tokens.at(_index);

			uint256 _oldTokenReserve = _self._getUnderlyingReserve(_token);
			uint256 _oldTokenPercent = _oldTokenReserve.mul(1e18).div(_reserveAmount);
			uint256 _newTokenPercent = _self.percents[_token];

			if (_newTokenPercent > _oldTokenPercent) {
				uint256 _percent = _newTokenPercent.sub(_oldTokenPercent);
				if (_percent > _maxPercent) {
					uint256 _newTokenReserve = _reserveAmount.mul(_newTokenPercent).div(1e18);
					uint256 _amount = _newTokenReserve.sub(_oldTokenReserve);

					_maxPercent = _percent;
					_adjustToken = _token;
					_adjustAmount = _amount;
				}
			}
		}

		return (_adjustToken, _adjustAmount);
	}

	/**
	 * @dev Search the list of gTokens and selects the one for which the
	 *      withdrawal will provide the best correction of deviation from
	 *      its target share.
	 * @param _reserveAmount The total reserve amount used for calculation.
	 * @return _adjustToken The gToken to withdraw from.
	 * @return _adjustAmount The amount to be withdrawn.
	 */
	function _findWithdrawal(Self storage _self, uint256 _reserveAmount) internal view returns (address _adjustToken, uint256 _adjustAmount)
	{
		uint256 _maxPercent = _self.portfolioRebalanceMargin;
		_adjustToken = address(0);
		_adjustAmount = 0;

		uint256 _tokenCount = _self.tokens.length();
		for (uint256 _index = 0; _index < _tokenCount; _index++) {
			address _token = _self.tokens.at(_index);

			uint256 _oldTokenReserve = _self._getUnderlyingReserve(_token);
			uint256 _oldTokenPercent = _oldTokenReserve.mul(1e18).div(_reserveAmount);
			uint256 _newTokenPercent = _self.percents[_token];

			if (_newTokenPercent < _oldTokenPercent) {
				uint256 _percent = _oldTokenPercent.sub(_newTokenPercent);
				if (_percent > _maxPercent) {
					uint256 _newTokenReserve = _reserveAmount.mul(_newTokenPercent).div(1e18);
					uint256 _amount = _oldTokenReserve.sub(_newTokenReserve);

					_maxPercent = _percent;
					_adjustToken = _token;
					_adjustAmount = _amount;
				}
			}
		}

		return (_adjustToken, _adjustAmount);
	}

	/**
	 * @dev Performs a deposit of the reserve asset to the given gToken.
	 * @param _token The gToken to deposit to.
	 * @param _amount The amount to be deposited.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _depositUnderlying(Self storage _self, address _token, uint256 _amount) internal returns (bool _success)
	{
		_amount = G.min(_amount, G.getBalance(_self.reserveToken));
		if (_amount == 0) return true;
		G.approveFunds(_self.reserveToken, _token, _amount);
		try GCToken(_token).depositUnderlying(_amount) {
			return true;
		} catch (bytes memory /* _data */) {
			G.approveFunds(_self.reserveToken, _token, 0);
			return false;
		}
	}

	/**
	 * @dev Performs a withdrawal of the reserve asset from the given gToken.
	 * @param _token The gToken to withdraw from.
	 * @param _amount The amount to be withdrawn.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _withdrawUnderlying(Self storage _self, address _token, uint256 _amount) internal returns (bool _success)
	{
		uint256 _grossShares = _self._calcWithdrawalSharesFromUnderlyingCost(_token, _amount);
		_grossShares = G.min(_grossShares, G.getBalance(_token));
		if (_grossShares == 0) return true;
		try GCToken(_token).withdrawUnderlying(_grossShares) {
			return true;
		} catch (bytes memory /* _data */) {
			return false;
		}
	}

	/**
	 * @dev Calculates how much of the reserve token is available for
	 *      withdrawal by the current contract for the given gToken.
	 * @param _token The gToken to withdraw from.
	 * @return _underlyingCost The total amount redeemable by the current
	 *                         contract from the given gToken.
	 */
	function _getUnderlyingReserve(Self storage _self, address _token) internal view returns (uint256 _underlyingCost)
	{
		uint256 _grossShares = G.getBalance(_token);
		return _self._calcWithdrawalUnderlyingCostFromShares(_token, _grossShares);
	}

	/**
	 * @dev Calculates how much will be received for withdrawing the provided
	 *      number of shares from a given gToken.
	 * @param _token The gToken to withdraw from.
	 * @param _grossShares The number of shares to be provided.
	 * @return _underlyingCost The amount to be received.
	 */
	function _calcWithdrawalUnderlyingCostFromShares(Self storage /* _self */, address _token, uint256 _grossShares) internal view returns (uint256 _underlyingCost)
	{
		uint256 _totalReserve = GCToken(_token).totalReserve();
		uint256 _totalSupply = GCToken(_token).totalSupply();
		uint256 _withdrawalFee = GCToken(_token).withdrawalFee();
		uint256 _exchangeRate = GCToken(_token).exchangeRate();
		(_underlyingCost,) = GCToken(_token).calcWithdrawalUnderlyingCostFromShares(_grossShares, _totalReserve, _totalSupply, _withdrawalFee, _exchangeRate);
		return _underlyingCost;
	}

	/**
	 * @dev Calculates how many shares are required to withdraw so much from
	 *      a given gToken.
	 * @param _token The gToken to withdraw from.
	 * @param _underlyingCost The desired amount to be withdrawn.
	 * @return _grossShares The number of shares required to withdraw the desired amount.
	 */
	function _calcWithdrawalSharesFromUnderlyingCost(Self storage /* _self */, address _token, uint256 _underlyingCost) internal view returns (uint256 _grossShares)
	{
		uint256 _totalReserve = GCToken(_token).totalReserve();
		uint256 _totalSupply = GCToken(_token).totalSupply();
		uint256 _withdrawalFee = GCToken(_token).withdrawalFee();
		uint256 _exchangeRate = GCToken(_token).exchangeRate();
		(_grossShares,) = GCToken(_token).calcWithdrawalSharesFromUnderlyingCost(_underlyingCost, _totalReserve, _totalSupply, _withdrawalFee, _exchangeRate);
		return _grossShares;
	}
}

// File: contracts/GTokenType0.sol

pragma solidity ^0.6.0;




/**
 * @notice This contract implements the functionality for the gToken Type 0.
 *         The gToken Type 0 provides a simple portfolio management strategy
 *         that splits the reserve asset percentually among multiple other
 *         gTokens. Also, it allows for part of the reserve to be kept liquid,
 *         in the reserve token itself, to save on gas fees. The contract owner
 *         can add and remove gTokens that compose the portfolio, as much as
 *         reconfigure their percentual shares. There is also a configurable
 *         rebalance margins that serves as threshold for when the contract will
 *         or not attempt to rebalance the reserve according to the set
 *         percentual ratios. The algorithm that maintains the proper
 *         distribution of the reserve token does so incrementally based on the
 *         following principles: 1) At each deposit/withdrawal, at most one
 *         underlying deposit/withdrawal is performed; 2) When the
 *         deposit/withdrawal can be served from the liquid pool, and within the
 *         bounds of the rebalance margin, no underlying deposit/withdrawal is
 *         performed; 3) When performing a rebalance the gToken with the
 *         most discrepant reserve share is chosen for rebalancing; 4) When
 *         performing an withdrawal, if it cannot be served entirely from
 *         the liquid pool, the we choose the gToken that can provide the
 *         required additional liquidity with the least percentual impact to
 *         its reserve share. As with all gTokens, gTokens Type 0 have an
 *         associated locked liquidity pool and follow the same fee structure.
 *         See GTokenBase and GPortfolioReserveManager for further documentation.
 */
contract GTokenType0 is GTokenBase, GPortfolio
{
	using GPortfolioReserveManager for GPortfolioReserveManager.Self;

	GPortfolioReserveManager.Self prm;

	/**
	 * @dev Constructor for the gToken Type 0 contract.
	 * @param _name The ERC-20 token name.
	 * @param _symbol The ERC-20 token symbol.
	 * @param _decimals The ERC-20 token decimals.
	 * @param _stakesToken The ERC-20 token address to be used as stakes
	 *                     token (GRO).
	 * @param _reserveToken The ERC-20 token address to be used as reserve
	 *                      token (e.g. DAI for gDAI).
	 */
	constructor (string memory _name, string memory _symbol, uint8 _decimals, address _stakesToken, address _reserveToken)
		GTokenBase(_name, _symbol, _decimals, _stakesToken, _reserveToken) public
	{
		prm.init(_reserveToken);
	}

	/**
	 * @notice Overrides the default total reserve definition in order to
	 *         account, not only for the reserve asset being kept liquid by
	 *         this contract, but also sum up the reserve portions delegated
	 *         to all gTokens that make up the portfolio.
	 * @return _totalReserve The amount of the reserve token corresponding
	 *                       to this contract's worth.
	 */
	function totalReserve() public view override returns (uint256 _totalReserve)
	{
		return prm.totalReserve();
	}

	/**
	 * @notice Provides the number of gTokens that were added to this
	 *         contract by the owner.
	 * @return _count The number of gTokens that make up the portfolio.
	 */
	function tokenCount() public view override returns (uint256 _count)
	{
		return prm.tokenCount();
	}

	/**
	 * @notice Provides a gToken that was added to this contract by the owner
	 *         at a given index. Note that the index to token association
	 *         is preserved in between token removals, however removals may
	 *         may shuffle it around.
	 * @param _index The desired index, must be less than the token count.
	 * @return _token The gToken currently present at the given index.
	 */
	function tokenAt(uint256 _index) public view override returns (address _token)
	{
		return prm.tokenAt(_index);
	}

	/**
	 * @notice Provides the percentual share of a gToken in the composition
	 *         of the portfolio. Note that the value returned is the desired
	 *         percentual share and not the actual reserve share.
	 * @param _token The given token address.
	 * @return _percent The token percentual share of the portfolio, as
	 *                  configured by the owner.
	 */
	function tokenPercent(address _token) public view override returns (uint256 _percent)
	{
		return prm.tokenPercent(_token);
	}

	/**
	 * @notice Provides the percentual margins tolerable before triggering a
	 *         rebalance action (i.e. an underlying deposit or withdrawal).
	 * @return _liquidRebalanceMargin The liquid percentual rebalance margin,
	 *                                as configured by the owner.
	 * @return _portfolioRebalanceMargin The portfolio percentual rebalance
	 *                                   margin, as configured by the owner.
	 */
	function getRebalanceMargins() public view override returns (uint256 _liquidRebalanceMargin, uint256 _portfolioRebalanceMargin)
	{
		return (prm.liquidRebalanceMargin, prm.portfolioRebalanceMargin);
	}

	/**
	 * @notice Inserts a new gToken into the portfolio. The new gToken must
	 *         have the reserve token as its underlying token. The initial
	 *         portfolio share of the new token will be 0%.
	 * @param _token The contract address of the new gToken to be incorporated
	 *               into the portfolio.
	 */
	function insertToken(address _token) public override onlyOwner nonReentrant
	{
		prm.insertToken(_token);
		emit InsertToken(_token);
	}

	/**
	 * @notice Removes a gToken from the portfolio. The portfolio share of
	 *         the token must be 0% before it can be removed. The underlying
	 *         reserve is redeemed upon removal.
	 * @param _token The contract address of the gToken to be removed from
	 *               the portfolio.
	 */
	function removeToken(address _token) public override onlyOwner nonReentrant
	{
		prm.removeToken(_token);
		emit RemoveToken(_token);
	}

	/**
	 * @notice Announces a token percent transfer before it can happen,
	 *         signaling the intention to modify the porfolio distribution.
	 * @param _sourceToken The token address to provide the share.
	 * @param _targetToken The token address to receive the share.
	 * @param _percent The percentual share to shift.
	 */
	function anounceTokenPercentTransfer(address _sourceToken, address _targetToken, uint256 _percent) public override onlyOwner nonReentrant
	{
		prm.announceTokenPercentTransfer(_sourceToken, _targetToken, _percent);
		emit AnnounceTokenPercentTransfer(_sourceToken, _targetToken, _percent);
	}

	/**
	 * @notice Shifts a percentual share of the portfolio allocation from
	 *         one gToken to another gToken. The reserve token can also be
	 *         used as source or target of the operation. This does not
	 *         actually shifts funds, only reconfigures the allocation.
	 * @param _sourceToken The token address to provide the share.
	 * @param _targetToken The token address to receive the share.
	 * @param _percent The percentual share to shift.
	 */
	function transferTokenPercent(address _sourceToken, address _targetToken, uint256 _percent) public override onlyOwner nonReentrant
	{
		uint256 _oldSourceTokenPercent = prm.tokenPercent(_sourceToken);
		uint256 _oldTargetTokenPercent = prm.tokenPercent(_targetToken);
		prm.transferTokenPercent(_sourceToken, _targetToken, _percent);
		uint256 _newSourceTokenPercent = prm.tokenPercent(_sourceToken);
		uint256 _newTargetTokenPercent = prm.tokenPercent(_targetToken);
		emit TransferTokenPercent(_sourceToken, _targetToken, _percent);
		emit ChangeTokenPercent(_sourceToken, _oldSourceTokenPercent, _newSourceTokenPercent);
		emit ChangeTokenPercent(_targetToken, _oldTargetTokenPercent, _newTargetTokenPercent);
	}

	/**
	 * @notice Sets the percentual margins tolerable before triggering a
	 *         rebalance action (i.e. an underlying deposit or withdrawal).
	 * @param _liquidRebalanceMargin The liquid percentual rebalance margin,
	 *                               to be configured by the owner.
	 * @param _portfolioRebalanceMargin The portfolio percentual rebalance
	 *                                  margin, to be configured by the owner.
	 */
	function setRebalanceMargins(uint256 _liquidRebalanceMargin, uint256 _portfolioRebalanceMargin) public override onlyOwner nonReentrant
	{
		prm.setRebalanceMargins(_liquidRebalanceMargin, _portfolioRebalanceMargin);
	}

	/**
	 * @dev This method is overriden from GTokenBase and sets up the reserve
	 *      after a deposit comes along. This method uses the GPortfolioReserveManager
	 *      to adjust the reserve implementing the rebalance policy.
	 *      See GPortfolioReserveManager.sol.
	 * @param _cost The amount of reserve being deposited (ignored).
	 * @return _success A boolean indicating whether or not the operation
	 *                  succeeded. This operation should not fail unless
	 *                  any of the underlying components (Compound, Aave,
	 *                  Dydx) also fails.
	 */
	function _prepareDeposit(uint256 _cost) internal override returns (bool _success)
	{
		_cost; // silences warnings
		return prm.adjustReserve(0);
	}

	/**
	 * @dev This method is overriden from GTokenBase and sets up the reserve
	 *      before a withdrawal comes along. This method uses the GPortfolioReserveManager
	 *      to adjust the reserve implementing the rebalance policy.
	 *      See GPortfolioReserveManager.sol.
	 * @param _cost The amount of reserve being withdrawn and that needs to
	 *              be immediately liquid.
	 * @return _success A boolean indicating whether or not the operation succeeded.
	 *                  The operation may fail if it is not possible to recover
	 *                  the required liquidity (e.g. low liquidity in the markets).
	 */
	function _prepareWithdrawal(uint256 _cost) internal override returns (bool _success)
	{
		return prm.adjustReserve(_cost);
	}
}

// File: contracts/GCFormulae.sol

pragma solidity ^0.6.0;



/**
 * @dev Pure implementation of deposit/minting and withdrawal/burning formulas
 *      for gTokens calculated based on the cToken underlying asset
 *      (e.g. DAI for cDAI). See GFormulae.sol and GCTokenBase.sol for further
 *      documentation.
 */
library GCFormulae
{
	using SafeMath for uint256;

	/**
	 * @dev Simple token to cToken formula from Compound
	 */
	function _calcCostFromUnderlyingCost(uint256 _underlyingCost, uint256 _exchangeRate) internal pure returns (uint256 _cost)
	{
		return _underlyingCost.mul(1e18).div(_exchangeRate);
	}

	/**
	 * @dev Simple cToken to token formula from Compound
	 */
	function _calcUnderlyingCostFromCost(uint256 _cost, uint256 _exchangeRate) internal pure returns (uint256 _underlyingCost)
	{
		return _cost.mul(_exchangeRate).div(1e18);
	}

	/**
	 * @dev Composition of the gToken deposit formula with the Compound
	 *      conversion formula to obtain the gcToken deposit formula in
	 *      terms of the cToken underlying asset.
	 */
	function _calcDepositSharesFromUnderlyingCost(uint256 _underlyingCost, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee, uint256 _exchangeRate) internal pure returns (uint256 _netShares, uint256 _feeShares)
	{
		uint256 _cost = _calcCostFromUnderlyingCost(_underlyingCost, _exchangeRate);
		return GFormulae._calcDepositSharesFromCost(_cost, _totalReserve, _totalSupply, _depositFee);
	}

	/**
	 * @dev Composition of the gToken reserve deposit formula with the
	 *      Compound conversion formula to obtain the gcToken reverse
	 *      deposit formula in terms of the cToken underlying asset.
	 */
	function _calcDepositUnderlyingCostFromShares(uint256 _netShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee, uint256 _exchangeRate) internal pure returns (uint256 _underlyingCost, uint256 _feeShares)
	{
		uint256 _cost;
		(_cost, _feeShares) = GFormulae._calcDepositCostFromShares(_netShares, _totalReserve, _totalSupply, _depositFee);
		return (_calcUnderlyingCostFromCost(_cost, _exchangeRate), _feeShares);
	}

	/**
	 * @dev Composition of the gToken reserve withdrawal formula with the
	 *      Compound conversion formula to obtain the gcToken reverse
	 *      withdrawal formula in terms of the cToken underlying asset.
	 */
	function _calcWithdrawalSharesFromUnderlyingCost(uint256 _underlyingCost, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee, uint256 _exchangeRate) internal pure returns (uint256 _grossShares, uint256 _feeShares)
	{
		uint256 _cost = _calcCostFromUnderlyingCost(_underlyingCost, _exchangeRate);
		return GFormulae._calcWithdrawalSharesFromCost(_cost, _totalReserve, _totalSupply, _withdrawalFee);
	}

	/**
	 * @dev Composition of the gToken withdrawal formula with the Compound
	 *      conversion formula to obtain the gcToken withdrawal formula in
	 *      terms of the cToken underlying asset.
	 */
	function _calcWithdrawalUnderlyingCostFromShares(uint256 _grossShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee, uint256 _exchangeRate) internal pure returns (uint256 _underlyingCost, uint256 _feeShares)
	{
		uint256 _cost;
		(_cost, _feeShares) = GFormulae._calcWithdrawalCostFromShares(_grossShares, _totalReserve, _totalSupply, _withdrawalFee);
		return (_calcUnderlyingCostFromCost(_cost, _exchangeRate), _feeShares);
	}
}

// File: contracts/GMining.sol

pragma solidity ^0.6.0;

/**
 * @dev An interface to extend gTokens with liquidity mining capabilities.
 *      See GCTokenBase.sol and GATokenBase.sol for further documentation.
 */
interface GMining
{
	// view functions
	function miningToken() external view returns (address _miningToken);
	function growthToken() external view returns (address _growthToken);
	function exchange() external view returns (address _exchange);
	function miningGulpRange() external view returns (uint256 _miningMinGulpAmount, uint256 _miningMaxGulpAmount);
	function growthGulpRange() external view returns (uint256 _growthMinGulpAmount, uint256 _growthMaxGulpAmount);

	// priviledged functions
	function setExchange(address _exchange) external;
	function setMiningGulpRange(uint256 _miningMinGulpAmount, uint256 _miningMaxGulpAmount) external;
	function setGrowthGulpRange(uint256 _growthMinGulpAmount, uint256 _growthMaxGulpAmount) external;
}

// File: contracts/interop/Compound.sol

pragma solidity ^0.6.0;


/**
 * @dev Minimal set of declarations for Compound interoperability.
 */
interface Comptroller
{
	function oracle() external view returns (address _oracle);
	function enterMarkets(address[] calldata _ctokens) external returns (uint256[] memory _errorCodes);
	function markets(address _ctoken) external view returns (bool _isListed, uint256 _collateralFactorMantissa);
	function getAccountLiquidity(address _account) external view returns (uint256 _error, uint256 _liquidity, uint256 _shortfall);
}

interface CPriceOracle
{
	function getUnderlyingPrice(address _ctoken) external view returns (uint256 _price);
}

interface CToken is IERC20
{
	function underlying() external view returns (address _token);
	function exchangeRateStored() external view returns (uint256 _exchangeRate);
	function borrowBalanceStored(address _account) external view returns (uint256 _borrowBalance);
	function exchangeRateCurrent() external returns (uint256 _exchangeRate);
	function getCash() external view returns (uint256 _cash);
	function borrowBalanceCurrent(address _account) external returns (uint256 _borrowBalance);
	function balanceOfUnderlying(address _owner) external returns (uint256 _underlyingBalance);
	function mint() external payable;
	function mint(uint256 _mintAmount) external returns (uint256 _errorCode);
	function repayBorrow() external payable;
	function repayBorrow(uint256 _repayAmount) external returns (uint256 _errorCode);
	function redeemUnderlying(uint256 _redeemAmount) external returns (uint256 _errorCode);
	function borrow(uint256 _borrowAmount) external returns (uint256 _errorCode);
}

// File: contracts/modules/CompoundLendingMarketAbstraction.sol

pragma solidity ^0.6.0;







/**
 * @dev This library abstracts the Compound lending market. It has a standardized
 *      lending market interface. See AaveLendingMarket.sol.
 */
library CompoundLendingMarketAbstraction
{
	using SafeMath for uint256;

	/**
	 * @dev Retreives an underlying token given a cToken.
	 * @param _ctoken The Compound cToken address.
	 * @return _token The underlying reserve token.
	 */
	function _getUnderlyingToken(address _ctoken) internal view returns (address _token)
	{
		if (_ctoken == $.cETH) return $.WETH;
		return CToken(_ctoken).underlying();
	}

	/**
	 * @dev Retrieves the maximum collateralization ratio for a given cToken.
	 * @param _ctoken The Compound cToken address.
	 * @return _collateralRatio The percentual ratio normalized to 1e18 (100%).
	 */
	function _getCollateralRatio(address _ctoken) internal view returns (uint256 _collateralRatio)
	{
		address _comptroller = $.Compound_COMPTROLLER;
		(, _collateralRatio) = Comptroller(_comptroller).markets(_ctoken);
		return _collateralRatio;
	}

	/**
	 * @dev Retrieves the current market liquidity for a given cToken.
	 * @param _ctoken The Compound cToken address.
	 * @return _marketAmount The underlying reserve token available
	 *                       market liquidity.
	 */
	function _getMarketAmount(address _ctoken) internal view returns (uint256 _marketAmount)
	{
		return CToken(_ctoken).getCash();
	}

	/**
	 * @dev Retrieves the current account liquidity in terms of a cToken
	 *      underlying reserve.
	 * @param _ctoken The Compound cToken address.
	 * @return _liquidityAmount The available account liquidity for the
	 *                          underlying reserve token.
	 */
	function _getLiquidityAmount(address _ctoken) internal view returns (uint256 _liquidityAmount)
	{
		address _comptroller = $.Compound_COMPTROLLER;
		(uint256 _result, uint256 _liquidity, uint256 _shortfall) = Comptroller(_comptroller).getAccountLiquidity(address(this));
		if (_result != 0) return 0;
		if (_shortfall > 0) return 0;
		address _priceOracle = Comptroller(_comptroller).oracle();
		uint256 _price = CPriceOracle(_priceOracle).getUnderlyingPrice(_ctoken);
		return _liquidity.mul(1e18).div(_price);
	}

	/**
	 * @dev Retrieves the calculated account liquidity in terms of a cToken
	 *      underlying reserve. It also considers the current market liquidity.
	 *      A safety margin can be provided to deflate the actual liquidity amount.
	 * @param _ctoken The Compound cToken address.
	 * @param _marginAmount The safety room to be left in terms of the
	 *                      underlying reserve token.
	 * @return _availableAmount The safe available liquidity in terms of the
	 *                          underlying reserve token.
	 */
	function _getAvailableAmount(address _ctoken, uint256 _marginAmount) internal view returns (uint256 _availableAmount)
	{
		uint256 _liquidityAmount = _getLiquidityAmount(_ctoken);
		if (_liquidityAmount <= _marginAmount) return 0;
		return Math._min(_liquidityAmount.sub(_marginAmount), _getMarketAmount(_ctoken));
	}

	/**
	 * @dev Retrieves the last read-only exchange rate between the cToken
	 *      and its underlying reserve token.
	 * @param _ctoken The Compound cToken address.
	 * @return _exchangeRate The exchange rate between the cToken and its
	 *                       underlying reserve token.
	 */
	function _getExchangeRate(address _ctoken) internal view returns (uint256 _exchangeRate)
	{
		return CToken(_ctoken).exchangeRateStored();
	}

	/**
	 * @dev Retrieves the last up-to-date exchange rate between the cToken
	 *      and its underlying reserve token.
	 * @param _ctoken The Compound cToken address.
	 * @return _exchangeRate The exchange rate between the cToken and its
	 *                       underlying reserve token.
	 */
	function _fetchExchangeRate(address _ctoken) internal returns (uint256 _exchangeRate)
	{
		return CToken(_ctoken).exchangeRateCurrent();
	}

	/**
	 * @dev Retrieves the last read-only value for the cToken lending
	 *      balance in terms of its underlying reserve token.
	 * @param _ctoken The Compound cToken address.
	 * @return _amount The lending balance in terms of the underlying
	 *                 reserve token.
	 */
	function _getLendAmount(address _ctoken) internal view returns (uint256 _amount)
	{
		return CToken(_ctoken).balanceOf(address(this)).mul(_getExchangeRate(_ctoken)).div(1e18);
	}

	/**
	 * @dev Retrieves the last up-to-date value for the cToken lending
	 *      balance in terms of its underlying reserve token.
	 * @param _ctoken The Compound cToken address.
	 * @return _amount The lending balance in terms of the underlying
	 *                 reserve token.
	 */
	function _fetchLendAmount(address _ctoken) internal returns (uint256 _amount)
	{
		return CToken(_ctoken).balanceOfUnderlying(address(this));
	}

	/**
	 * @dev Retrieves the last read-only value for the cToken borrowing
	 *      balance in terms of its underlying reserve token.
	 * @param _ctoken The Compound cToken address.
	 * @return _amount The borrowing balance in terms of the underlying
	 *                 reserve token.
	 */
	function _getBorrowAmount(address _ctoken) internal view returns (uint256 _amount)
	{
		return CToken(_ctoken).borrowBalanceStored(address(this));
	}

	/**
	 * @dev Retrieves the last up-to-date value for the cToken borrowing
	 *      balance in terms of its underlying reserve token.
	 * @param _ctoken The Compound cToken address.
	 * @return _amount The borrowing balance in terms of the underlying
	 *                 reserve token.
	 */
	function _fetchBorrowAmount(address _ctoken) internal returns (uint256 _amount)
	{
		return CToken(_ctoken).borrowBalanceCurrent(address(this));
	}

	/**
	 * @dev Signals the usage of a given cToken underlying reserve as
	 *      collateral for borrowing funds in the lending market.
	 * @param _ctoken The Compound cToken address.
	 * @return _success A boolean indicating whether or not the operation suceeded.
	 */
	function _enter(address _ctoken) internal returns (bool _success)
	{
		address _comptroller = $.Compound_COMPTROLLER;
		address[] memory _ctokens = new address[](1);
		_ctokens[0] = _ctoken;
		try Comptroller(_comptroller).enterMarkets(_ctokens) returns (uint256[] memory _errorCodes) {
			return _errorCodes[0] == 0;
		} catch (bytes memory /* _data */) {
			return false;
		}
	}

	/**
	 * @dev Lend funds to a given cToken's market.
	 * @param _ctoken The Compound cToken address.
	 * @param _amount The amount of the underlying token to lend.
	 * @return _success A boolean indicating whether or not the operation suceeded.
	 */
	function _lend(address _ctoken, uint256 _amount) internal returns (bool _success)
	{
		if (_ctoken == $.cETH) {
			if (!Wrapping._unwrap(_amount)) return false;
			try CToken(_ctoken).mint{value: _amount}() {
				return true;
			} catch (bytes memory /* _data */) {
				assert(Wrapping._wrap(_amount));
				return false;
			}
		} else {
			address _token = _getUnderlyingToken(_ctoken);
			Transfers._approveFunds(_token, _ctoken, _amount);
			try CToken(_ctoken).mint(_amount) returns (uint256 _errorCode) {
				return _errorCode == 0;
			} catch (bytes memory /* _data */) {
				Transfers._approveFunds(_token, _ctoken, 0);
				return false;
			}
		}
	}

	/**
	 * @dev Redeem funds lent to a given cToken's market.
	 * @param _ctoken The Compound cToken address.
	 * @param _amount The amount of the underlying token to redeem.
	 * @return _success A boolean indicating whether or not the operation suceeded.
	 */
	function _redeem(address _ctoken, uint256 _amount) internal returns (bool _success)
	{
		if (_ctoken == $.cETH) {
			try CToken(_ctoken).redeemUnderlying(_amount) returns (uint256 _errorCode) {
				if (_errorCode == 0) {
					assert(Wrapping._wrap(_amount));
					return true;
				} else {
					return false;
				}
			} catch (bytes memory /* _data */) {
				return false;
			}
		} else {
			try CToken(_ctoken).redeemUnderlying(_amount) returns (uint256 _errorCode) {
				return _errorCode == 0;
			} catch (bytes memory /* _data */) {
				return false;
			}
		}
	}

	/**
	 * @dev Borrow funds from a given cToken's market.
	 * @param _ctoken The Compound cToken address.
	 * @param _amount The amount of the underlying token to borrow.
	 * @return _success A boolean indicating whether or not the operation suceeded.
	 */
	function _borrow(address _ctoken, uint256 _amount) internal returns (bool _success)
	{
		if (_ctoken == $.cETH) {
			try CToken(_ctoken).borrow(_amount) returns (uint256 _errorCode) {
				if (_errorCode == 0) {
					assert(Wrapping._wrap(_amount));
					return true;
				} else {
					return false;
				}
			} catch (bytes memory /* _data */) {
				return false;
			}
		} else {
			try CToken(_ctoken).borrow(_amount) returns (uint256 _errorCode) {
				return _errorCode == 0;
			} catch (bytes memory /* _data */) {
				return false;
			}
		}
	}

	/**
	 * @dev Repays a loan taken from a given cToken's market.
	 * @param _ctoken The Compound cToken address.
	 * @param _amount The amount of the underlying token to repay.
	 * @return _success A boolean indicating whether or not the operation suceeded.
	 */
	function _repay(address _ctoken, uint256 _amount) internal returns (bool _success)
	{
		if (_ctoken == $.cETH) {
			if (!Wrapping._unwrap(_amount)) return false;
			try CToken(_ctoken).repayBorrow{value: _amount}() {
				return true;
			} catch (bytes memory /* _data */) {
				assert(Wrapping._wrap(_amount));
				return false;
			}
		} else {
			address _token = _getUnderlyingToken(_ctoken);
			Transfers._approveFunds(_token, _ctoken, _amount);
			try CToken(_ctoken).repayBorrow(_amount) returns (uint256 _errorCode) {
				return _errorCode == 0;
			} catch (bytes memory /* _data */) {
				Transfers._approveFunds(_token, _ctoken, 0);
				return false;
			}
		}
	}

	/**
	 * @dev Signals the usage of a given cToken underlying reserve as
	 *      collateral for borrowing funds in the lending market. This
	 *      operation will revert if it does not succeed.
	 * @param _ctoken The Compound cToken address.
	 */
	function _safeEnter(address _ctoken) internal
	{
		require(_enter(_ctoken), "enter failed");
	}

	/**
	 * @dev Lend funds to a given cToken's market. This
	 *      operation will revert if it does not succeed.
	 * @param _ctoken The Compound cToken address.
	 * @param _amount The amount of the underlying token to lend.
	 */
	function _safeLend(address _ctoken, uint256 _amount) internal
	{
		require(_lend(_ctoken, _amount), "lend failure");
	}

	/**
	 * @dev Redeem funds lent to a given cToken's market. This
	 *      operation will revert if it does not succeed.
	 * @param _ctoken The Compound cToken address.
	 * @param _amount The amount of the underlying token to redeem.
	 */
	function _safeRedeem(address _ctoken, uint256 _amount) internal
	{
		require(_redeem(_ctoken, _amount), "redeem failure");
	}

	/**
	 * @dev Borrow funds from a given cToken's market. This
	 *      operation will revert if it does not succeed.
	 * @param _ctoken The Compound cToken address.
	 * @param _amount The amount of the underlying token to borrow.
	 */
	function _safeBorrow(address _ctoken, uint256 _amount) internal
	{
		require(_borrow(_ctoken, _amount), "borrow failure");
	}

	/**
	 * @dev Repays a loan taken from a given cToken's market. This
	 *      operation will revert if it does not succeed.
	 * @param _ctoken The Compound cToken address.
	 * @param _amount The amount of the underlying token to repay.
	 */
	function _safeRepay(address _ctoken, uint256 _amount) internal
	{
		require(_repay(_ctoken, _amount), "repay failure");
	}
}

// File: contracts/GC.sol

pragma solidity ^0.6.0;


/**
 * @dev This public library provides a single entrypoint to the Compound lending
 *      market internal library available in the modules folder. It is a
 *      complement to the G.sol library. Both libraries exists to circunvent the
 *      contract size limitation imposed by the EVM. See G.sol for further
 *      documentation.
 */
library GC
{
	function getUnderlyingToken(address _ctoken) public view returns (address _token) { return CompoundLendingMarketAbstraction._getUnderlyingToken(_ctoken); }
	function getCollateralRatio(address _ctoken) public view returns (uint256 _collateralFactor) { return CompoundLendingMarketAbstraction._getCollateralRatio(_ctoken); }
	function getMarketAmount(address _ctoken) public view returns (uint256 _marketAmount) { return CompoundLendingMarketAbstraction._getMarketAmount(_ctoken); }
	function getLiquidityAmount(address _ctoken) public view returns (uint256 _liquidityAmount) { return CompoundLendingMarketAbstraction._getLiquidityAmount(_ctoken); }
	function getExchangeRate(address _ctoken) public view returns (uint256 _exchangeRate) { return CompoundLendingMarketAbstraction._getExchangeRate(_ctoken); }
	function fetchExchangeRate(address _ctoken) public returns (uint256 _exchangeRate) { return CompoundLendingMarketAbstraction._fetchExchangeRate(_ctoken); }
	function getLendAmount(address _ctoken) public view returns (uint256 _amount) { return CompoundLendingMarketAbstraction._getLendAmount(_ctoken); }
	function fetchLendAmount(address _ctoken) public returns (uint256 _amount) { return CompoundLendingMarketAbstraction._fetchLendAmount(_ctoken); }
	function getBorrowAmount(address _ctoken) public view returns (uint256 _amount) { return CompoundLendingMarketAbstraction._getBorrowAmount(_ctoken); }
	function fetchBorrowAmount(address _ctoken) public returns (uint256 _amount) { return CompoundLendingMarketAbstraction._fetchBorrowAmount(_ctoken); }
	function lend(address _ctoken, uint256 _amount) public returns (bool _success) { return CompoundLendingMarketAbstraction._lend(_ctoken, _amount); }
	function redeem(address _ctoken, uint256 _amount) public returns (bool _success) { return CompoundLendingMarketAbstraction._redeem(_ctoken, _amount); }
	function borrow(address _ctoken, uint256 _amount) public returns (bool _success) { return CompoundLendingMarketAbstraction._borrow(_ctoken, _amount); }
	function repay(address _ctoken, uint256 _amount) public returns (bool _success) { return CompoundLendingMarketAbstraction._repay(_ctoken, _amount); }
	function safeEnter(address _ctoken) public { CompoundLendingMarketAbstraction._safeEnter(_ctoken); }
	function safeLend(address _ctoken, uint256 _amount) public { CompoundLendingMarketAbstraction._safeLend(_ctoken, _amount); }
	function safeRedeem(address _ctoken, uint256 _amount) public { CompoundLendingMarketAbstraction._safeRedeem(_ctoken, _amount); }
}

// File: contracts/GCTokenBase.sol

pragma solidity ^0.6.0;









/**
 * @notice This abstract contract provides the basis implementation for all
 *         gcTokens, i.e. gTokens that use Compound cTokens as reserve, and
 *         implements the common functionality shared amongst them.
 *         In a nutshell, it extends the functinality of the GTokenBase contract
 *         to support operating directly using the cToken underlying asset.
 *         Therefore this contract provides functions that encapsulate minting
 *         and redeeming of cTokens internally, allowing users to interact with
 *         the contract providing funds directly in their underlying asset.
 */
abstract contract GCTokenBase is GTokenBase, GCToken, GMining
{
	address public immutable override miningToken;
	address public immutable override growthToken;
	address public immutable override underlyingToken;

	/**
	 * @dev Constructor for the gcToken contract.
	 * @param _name The ERC-20 token name.
	 * @param _symbol The ERC-20 token symbol.
	 * @param _decimals The ERC-20 token decimals.
	 * @param _stakesToken The ERC-20 token address to be used as stakes
	 *                     token (GRO).
	 * @param _reserveToken The ERC-20 token address to be used as reserve
	 *                      token (e.g. cDAI for gcDAI).
	 * @param _miningToken The ERC-20 token used for liquidity mining on
	 *                     compound (COMP).
	 * @param _growthToken The ERC-20 token address of the associated
	 *                     gToken, for gcTokens Type 2, or address(0),
	 *                     if this contract is a gcToken Type 1.
	 */
	constructor (string memory _name, string memory _symbol, uint8 _decimals, address _stakesToken, address _reserveToken, address _miningToken, address _growthToken)
		GTokenBase(_name, _symbol, _decimals, _stakesToken, _reserveToken) public
	{
		miningToken = _miningToken;
		growthToken = _growthToken;
		address _underlyingToken = GC.getUnderlyingToken(_reserveToken);
		underlyingToken = _underlyingToken;
	}

	/**
	 * @notice Allows for the beforehand calculation of the cToken amount
	 *         given the amount of the underlying token and an exchange rate.
	 * @param _underlyingCost The cost in terms of the cToken underlying asset.
	 * @param _exchangeRate The given exchange rate as provided by exchangeRate().
	 * @return _cost The equivalent cost in terms of cToken
	 */
	function calcCostFromUnderlyingCost(uint256 _underlyingCost, uint256 _exchangeRate) public pure override returns (uint256 _cost)
	{
		return GCFormulae._calcCostFromUnderlyingCost(_underlyingCost, _exchangeRate);
	}

	/**
	 * @notice Allows for the beforehand calculation of the underlying token
	 *         amount given the cToken amount and an exchange rate.
	 * @param _cost The cost in terms of the cToken.
	 * @param _exchangeRate The given exchange rate as provided by exchangeRate().
	 * @return _underlyingCost The equivalent cost in terms of the cToken underlying asset.
	 */
	function calcUnderlyingCostFromCost(uint256 _cost, uint256 _exchangeRate) public pure override returns (uint256 _underlyingCost)
	{
		return GCFormulae._calcUnderlyingCostFromCost(_cost, _exchangeRate);
	}

	/**
	 * @notice Allows for the beforehand calculation of shares to be
	 *         received/minted upon depositing the underlying asset to the
	 *         contract.
	 * @param _underlyingCost The amount of the underlying asset being deposited.
	 * @param _totalReserve The reserve balance as obtained by totalReserve().
	 * @param _totalSupply The shares supply as obtained by totalSupply().
	 * @param _depositFee The current deposit fee as obtained by depositFee().
	 * @param _exchangeRate The exchange rate as obtained by exchangeRate().
	 * @return _netShares The net amount of shares being received.
	 * @return _feeShares The fee amount of shares being deducted.
	 */
	function calcDepositSharesFromUnderlyingCost(uint256 _underlyingCost, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee, uint256 _exchangeRate) public pure override returns (uint256 _netShares, uint256 _feeShares)
	{
		return GCFormulae._calcDepositSharesFromUnderlyingCost(_underlyingCost, _totalReserve, _totalSupply, _depositFee, _exchangeRate);
	}

	/**
	 * @notice Allows for the beforehand calculation of the amount of the
	 *         underlying asset to be deposited in order to receive the desired
	 *         amount of shares.
	 * @param _netShares The amount of this gcToken shares to receive.
	 * @param _totalReserve The reserve balance as obtained by totalReserve().
	 * @param _totalSupply The shares supply as obtained by totalSupply().
	 * @param _depositFee The current deposit fee as obtained by depositFee().
	 * @param _exchangeRate The exchange rate as obtained by exchangeRate().
	 * @return _underlyingCost The cost, in the underlying asset, to be paid.
	 * @return _feeShares The fee amount of shares being deducted.
	 */
	function calcDepositUnderlyingCostFromShares(uint256 _netShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _depositFee, uint256 _exchangeRate) public pure override returns (uint256 _underlyingCost, uint256 _feeShares)
	{
		return GCFormulae._calcDepositUnderlyingCostFromShares(_netShares, _totalReserve, _totalSupply, _depositFee, _exchangeRate);
	}

	/**
	 * @notice Allows for the beforehand calculation of shares to be
	 *         given/burned upon withdrawing the underlying asset from the
	 *         contract.
	 * @param _underlyingCost The amount of the underlying asset being withdrawn.
	 * @param _totalReserve The reserve balance as obtained by totalReserve()
	 * @param _totalSupply The shares supply as obtained by totalSupply()
	 * @param _withdrawalFee The current withdrawl fee as obtained by withdrawalFee()
	 * @param _exchangeRate The exchange rate as obtained by exchangeRate().
	 * @return _grossShares The total amount of shares being deducted,
	 *                      including fees.
	 * @return _feeShares The fee amount of shares being deducted.
	 */
	function calcWithdrawalSharesFromUnderlyingCost(uint256 _underlyingCost, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee, uint256 _exchangeRate) public pure override returns (uint256 _grossShares, uint256 _feeShares)
	{
		return GCFormulae._calcWithdrawalSharesFromUnderlyingCost(_underlyingCost, _totalReserve, _totalSupply, _withdrawalFee, _exchangeRate);
	}

	/**
	 * @notice Allows for the beforehand calculation of the amount of the
	 *         underlying asset to be withdrawn given the desired amount of
	 *         shares.
	 * @param _grossShares The amount of this gcToken shares to provide.
	 * @param _totalReserve The reserve balance as obtained by totalReserve().
	 * @param _totalSupply The shares supply as obtained by totalSupply().
	 * @param _withdrawalFee The current withdrawal fee as obtained by withdrawalFee().
	 * @param _exchangeRate The exchange rate as obtained by exchangeRate().
	 * @return _underlyingCost The cost, in the underlying asset, to be received.
	 * @return _feeShares The fee amount of shares being deducted.
	 */
	function calcWithdrawalUnderlyingCostFromShares(uint256 _grossShares, uint256 _totalReserve, uint256 _totalSupply, uint256 _withdrawalFee, uint256 _exchangeRate) public pure override returns (uint256 _underlyingCost, uint256 _feeShares)
	{
		return GCFormulae._calcWithdrawalUnderlyingCostFromShares(_grossShares, _totalReserve, _totalSupply, _withdrawalFee, _exchangeRate);
	}

	/**
	 * @notice Provides the Compound exchange rate since their last update.
	 * @return _exchangeRate The exchange rate between cToken and its
	 *                       underlying asset
	 */
	function exchangeRate() public view override returns (uint256 _exchangeRate)
	{
		return GC.getExchangeRate(reserveToken);
	}

	/**
	 * @notice Provides the total amount kept in the reserve in terms of the
	 *         underlying asset.
	 * @return _totalReserveUnderlying The underlying asset balance on reserve.
	 */
	function totalReserveUnderlying() public view virtual override returns (uint256 _totalReserveUnderlying)
	{
		return GCFormulae._calcUnderlyingCostFromCost(totalReserve(), exchangeRate());
	}

	/**
	 * @notice Provides the total amount of the underlying asset (or equivalent)
	 *         this contract is currently lending on Compound.
	 * @return _lendingReserveUnderlying The underlying asset lending
	 *                                   balance on Compound.
	 */
	function lendingReserveUnderlying() public view virtual override returns (uint256 _lendingReserveUnderlying)
	{
		return GC.getLendAmount(reserveToken);
	}

	/**
	 * @notice Provides the total amount of the underlying asset (or equivalent)
	 *         this contract is currently borrowing on Compound.
	 * @return _borrowingReserveUnderlying The underlying asset borrowing
	 *                                     balance on Compound.
	 */
	function borrowingReserveUnderlying() public view virtual override returns (uint256 _borrowingReserveUnderlying)
	{
		return GC.getBorrowAmount(reserveToken);
	}

	/**
	 * @notice Performs the minting of gcToken shares upon the deposit of the
	 *         cToken underlying asset. The funds will be pulled in by this
	 *         contract, therefore they must be previously approved. This
	 *         function builds upon the GTokenBase deposit function. See
	 *         GTokenBase.sol for further documentation.
	 * @param _underlyingCost The amount of the underlying asset being
	 *                        deposited in the operation.
	 */
	function depositUnderlying(uint256 _underlyingCost) public override nonReentrant
	{
		address _from = msg.sender;
		require(_underlyingCost > 0, "underlying cost must be greater than 0");
		uint256 _cost = GCFormulae._calcCostFromUnderlyingCost(_underlyingCost, exchangeRate());
		(uint256 _netShares, uint256 _feeShares) = GFormulae._calcDepositSharesFromCost(_cost, totalReserve(), totalSupply(), depositFee());
		require(_netShares > 0, "shares must be greater than 0");
		G.pullFunds(underlyingToken, _from, _underlyingCost);
		GC.safeLend(reserveToken, _underlyingCost);
		require(_prepareDeposit(_cost), "not available at the moment");
		_mint(_from, _netShares);
		_mint(address(this), _feeShares.div(2));
	}

	/**
	 * @notice Performs the burning of gcToken shares upon the withdrawal of
	 *         the underlying asset. This function builds upon the
	 *         GTokenBase withdrawal function. See GTokenBase.sol for
	 *         further documentation.
	 * @param _grossShares The gross amount of this gcToken shares being
	 *                     redeemed in the operation.
	 */
	function withdrawUnderlying(uint256 _grossShares) public override nonReentrant
	{
		address _from = msg.sender;
		require(_grossShares > 0, "shares must be greater than 0");
		(uint256 _cost, uint256 _feeShares) = GFormulae._calcWithdrawalCostFromShares(_grossShares, totalReserve(), totalSupply(), withdrawalFee());
		uint256 _underlyingCost = GCFormulae._calcUnderlyingCostFromCost(_cost, exchangeRate());
		require(_underlyingCost > 0, "underlying cost must be greater than 0");
		require(_prepareWithdrawal(_cost), "not available at the moment");
		_underlyingCost = G.min(_underlyingCost, GC.getLendAmount(reserveToken));
		GC.safeRedeem(reserveToken, _underlyingCost);
		G.pushFunds(underlyingToken, _from, _underlyingCost);
		_burn(_from, _grossShares);
		_mint(address(this), _feeShares.div(2));
	}

	/**
	 * @dev The default behavior of this function is to send the funds to
	 *      address(0), but we override it and send the funds to the stkGRO
	 *      contract instead.
	 * @param _stakesAmount The amount of the stakes token being burned.
	 */
	function _burnStakes(uint256 _stakesAmount) internal override
	{
		G.pushFunds(stakesToken, $.stkGRO, _stakesAmount);
	}
}

// File: contracts/GCLeveragedReserveManager.sol

pragma solidity ^0.6.0;




/**
 * @dev This library implements data structure abstraction for the leveraged
 *      reserve management code in order to circuvent the EVM contract size limit.
 *      It is therefore a public library shared by all gcToken Type 1 contracts and
 *      needs to be published alongside them. See GCTokenType1.sol for further
 *      documentation.
 */
library GCLeveragedReserveManager
{
	using SafeMath for uint256;
	using GCLeveragedReserveManager for GCLeveragedReserveManager.Self;

	uint256 constant MAXIMUM_COLLATERALIZATION_RATIO = 98e16; // 98% of 75% = 73.5%
	uint256 constant DEFAULT_COLLATERALIZATION_RATIO = 94e16; // 94% of 75% = 70.5%
	uint256 constant DEFAULT_COLLATERALIZATION_MARGIN = 2e16; // 2% of 75% = 1.5%

	struct Self {
		address reserveToken;
		address underlyingToken;

		address exchange;

		address miningToken;
		uint256 miningMinGulpAmount;
		uint256 miningMaxGulpAmount;

		uint256 collateralizationRatio;
		uint256 collateralizationMargin;
	}

	/**
	 * @dev Initializes the data structure. This method is exposed publicly.
	 * @param _reserveToken The ERC-20 token address of the reserve token (cToken).
	 * @param _miningToken The ERC-20 token address to be collected from
	 *                     liquidity mining (COMP).
	 */
	function init(Self storage _self, address _reserveToken, address _miningToken) public
	{
		address _underlyingToken = GC.getUnderlyingToken(_reserveToken);

		_self.reserveToken = _reserveToken;
		_self.underlyingToken = _underlyingToken;

		_self.exchange = address(0);

		_self.miningToken = _miningToken;
		_self.miningMinGulpAmount = 0;
		_self.miningMaxGulpAmount = 0;

		_self.collateralizationRatio = DEFAULT_COLLATERALIZATION_RATIO;
		_self.collateralizationMargin = DEFAULT_COLLATERALIZATION_MARGIN;

		GC.safeEnter(_reserveToken);
	}

	/**
	 * @dev Sets the contract address for asset conversion delegation.
	 *      This library converts the miningToken into the underlyingToken
	 *      and use the assets to back the reserveToken. See GExchange.sol
	 *      for further documentation. This method is exposed publicly.
	 * @param _exchange The address of the contract that implements the
	 *                  GExchange interface.
	 */
	function setExchange(Self storage _self, address _exchange) public
	{
		_self.exchange = _exchange;
	}

	/**
	 * @dev Sets the range for converting liquidity mining assets. This
	 *      method is exposed publicly.
	 * @param _miningMinGulpAmount The minimum amount, funds will only be
	 *                             converted once the minimum is accumulated.
	 * @param _miningMaxGulpAmount The maximum amount, funds beyond this
	 *                             limit will not be converted and are left
	 *                             for future rounds of conversion.
	 */
	function setMiningGulpRange(Self storage _self, uint256 _miningMinGulpAmount, uint256 _miningMaxGulpAmount) public
	{
		require(_miningMinGulpAmount <= _miningMaxGulpAmount, "invalid range");
		_self.miningMinGulpAmount = _miningMinGulpAmount;
		_self.miningMaxGulpAmount = _miningMaxGulpAmount;
	}

	/**
	 * @dev Sets the collateralization ratio and margin. These values are
	 *      percentual and relative to the maximum collateralization ratio
	 *      provided by the underlying asset. This method is exposed publicly.
	 * @param _collateralizationRatio The target collateralization ratio,
	 *                                between lend and borrow, that the
	 *                                reserve will try to maintain.
	 * @param _collateralizationMargin The deviation from the target ratio
	 *                                 that should be accepted.
	 */
	function setCollateralizationRatio(Self storage _self, uint256 _collateralizationRatio, uint256 _collateralizationMargin) public
	{
		require(_collateralizationMargin <= _collateralizationRatio && _collateralizationRatio.add(_collateralizationMargin) <= MAXIMUM_COLLATERALIZATION_RATIO, "invalid ratio");
		_self.collateralizationRatio = _collateralizationRatio;
		_self.collateralizationMargin = _collateralizationMargin;
	}

	/**
	 * @dev Performs the reserve adjustment actions leaving a liquidity room,
	 *      if necessary. It will attempt to incorporate the liquidity mining
	 *      assets into the reserve and adjust the collateralization
	 *      targeting the configured ratio. This method is exposed publicly.
	 * @param _roomAmount The underlying token amount to be available after the
	 *                    operation. This is revelant for withdrawals, once the
	 *                    room amount is withdrawn the reserve should reflect
	 *                    the configured collateralization ratio.
	 * @return _success A boolean indicating whether or not both actions suceeded.
	 */
	function adjustReserve(Self storage _self, uint256 _roomAmount) public returns (bool _success)
	{
		bool success1 = _self._gulpMiningAssets();
		bool success2 = _self._adjustLeverage(_roomAmount);
		return success1 && success2;
	}

	/**
	 * @dev Calculates the collateralization ratio and range relative to the
	 *      maximum collateralization ratio provided by the underlying asset.
	 * @return _collateralizationRatio The target absolute collateralization ratio.
	 * @return _minCollateralizationRatio The minimum absolute collateralization ratio.
	 * @return _maxCollateralizationRatio The maximum absolute collateralization ratio.
	 */
	function _calcCollateralizationRatio(Self storage _self) internal view returns (uint256 _collateralizationRatio, uint256 _minCollateralizationRatio, uint256 _maxCollateralizationRatio)
	{
		uint256 _collateralRatio = GC.getCollateralRatio(_self.reserveToken);
		_collateralizationRatio = _collateralRatio.mul(_self.collateralizationRatio).div(1e18);
		_minCollateralizationRatio = _collateralRatio.mul(_self.collateralizationRatio.sub(_self.collateralizationMargin)).div(1e18);
		_maxCollateralizationRatio = _collateralRatio.mul(_self.collateralizationRatio.add(_self.collateralizationMargin)).div(1e18);
		return (_collateralizationRatio, _minCollateralizationRatio, _maxCollateralizationRatio);
	}

	/**
	 * @dev Incorporates the liquidity mining assets into the reserve. Assets
	 *      are converted to the underlying asset and then added to the reserve.
	 *      If the amount available is below the minimum, or if the exchange
	 *      contract is not set, nothing is done. Otherwise the operation is
	 *      performed, limited to the maximum amount. Note that this operation
	 *      will incorporate to the reserve all the underlying token balance
	 *      including funds sent to it or left over somehow.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _gulpMiningAssets(Self storage _self) internal returns (bool _success)
	{
		if (_self.exchange == address(0)) return true;
		if (_self.miningMaxGulpAmount == 0) return true;
		uint256 _miningAmount = G.getBalance(_self.miningToken);
		if (_miningAmount == 0) return true;
		if (_miningAmount < _self.miningMinGulpAmount) return true;
		_self._convertMiningToUnderlying(G.min(_miningAmount, _self.miningMaxGulpAmount));
		return GC.lend(_self.reserveToken, G.getBalance(_self.underlyingToken));
	}

	/**
	 * @dev Adjusts the reserve to match the configured collateralization
	 *      ratio. It calculates how much the collateralization must be
	 *      increased or decreased and either: 1) lend/borrow, or
	 *      2) repay/redeem, respectivelly. The funds required to perform
	 *      the operation are obtained via FlashLoan to avoid having to
	 *      maneuver around margin when moving in/out of leverage.
	 * @param _roomAmount The amount of underlying token to be liquid after
	 *                    the operation.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _adjustLeverage(Self storage _self, uint256 _roomAmount) internal returns (bool _success)
	{
		// the reserve is the diference between lend and borrow
		uint256 _lendAmount = GC.fetchLendAmount(_self.reserveToken);
		uint256 _borrowAmount = GC.fetchBorrowAmount(_self.reserveToken);
		uint256 _reserveAmount = _lendAmount.sub(_borrowAmount);
		// caps the room in case it is larger than the reserve
		_roomAmount = G.min(_roomAmount, _reserveAmount);
		// The new reserve must deduct the room requested
		uint256 _newReserveAmount = _reserveAmount.sub(_roomAmount);
		// caculates the assumed lend amount deducting the requested room
		uint256 _oldLendAmount = _lendAmount.sub(_roomAmount);
		// the new lend amount is the new reserve with leverage applied
		uint256 _newLendAmount;
		uint256 _minNewLendAmount;
		uint256 _maxNewLendAmount;
		{
			(uint256 _collateralizationRatio, uint256 _minCollateralizationRatio, uint256 _maxCollateralizationRatio) = _self._calcCollateralizationRatio();
			_newLendAmount = _newReserveAmount.mul(1e18).div(uint256(1e18).sub(_collateralizationRatio));
			_minNewLendAmount = _newReserveAmount.mul(1e18).div(uint256(1e18).sub(_minCollateralizationRatio));
			_maxNewLendAmount = _newReserveAmount.mul(1e18).div(uint256(1e18).sub(_maxCollateralizationRatio));
		}
		// adjust the reserve by:
		// 1- increasing collateralization by the difference
		// 2- decreasing collateralization by the difference
		// the adjustment is capped by the liquidity available on the market
		uint256 _liquidityAmount = G.getFlashLoanLiquidity(_self.underlyingToken);
		if (_minNewLendAmount > _oldLendAmount) {
			{
				uint256 _minAmount = _minNewLendAmount.sub(_oldLendAmount);
				require(_liquidityAmount >= _minAmount, "cannot maintain collateralization ratio");
			}
			uint256 _amount = _newLendAmount.sub(_oldLendAmount);
			return _self._dispatchFlashLoan(G.min(_amount, _liquidityAmount), 1);
		}
		if (_maxNewLendAmount < _oldLendAmount) {
			{
				uint256 _minAmount = _oldLendAmount.sub(_maxNewLendAmount);
				require(_liquidityAmount >= _minAmount, "cannot maintain collateralization ratio");
			}
			uint256 _amount = _oldLendAmount.sub(_newLendAmount);
			return _self._dispatchFlashLoan(G.min(_amount, _liquidityAmount), 2);
		}
		return true;
	}

	/**
	 * @dev This is the continuation of _adjustLeverage once funds are
	 *      borrowed via the FlashLoan callback.
	 * @param _amount The borrowed amount as requested.
	 * @param _fee The additional fee that needs to be paid for the FlashLoan.
	 * @param _which A flag indicating whether the funds were borrowed to
	 *               1) increase or 2) decrease the collateralization ratio.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _continueAdjustLeverage(Self storage _self, uint256 _amount, uint256 _fee, uint256 _which) internal returns (bool _success)
	{
		// note that the reserve adjustment is not 100% accurate as we
		// did not account for FlashLoan fees in the initial calculation
		if (_which == 1) {
			bool _success1 = GC.lend(_self.reserveToken, _amount.sub(_fee));
			bool _success2 = GC.borrow(_self.reserveToken, _amount);
			return _success1 && _success2;
		}
		if (_which == 2) {
			bool _success1 = GC.repay(_self.reserveToken, _amount);
			bool _success2 = GC.redeem(_self.reserveToken, _amount.add(_fee));
			return _success1 && _success2;
		}
		assert(false);
	}

	/**
	 * @dev Abstracts the details of dispatching the FlashLoan by encoding
	 *      the extra parameters.
	 * @param _amount The amount to be borrowed.
	 * @param _which A flag indicating whether the funds are borrowed to
	 *               1) increase or 2) decrease the collateralization ratio.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _dispatchFlashLoan(Self storage _self, uint256 _amount, uint256 _which) internal returns (bool _success)
	{
		return G.requestFlashLoan(_self.underlyingToken, _amount, abi.encode(_which));
	}

	/**
	 * @dev Abstracts the details of receiving a FlashLoan by decoding
	 *      the extra parameters.
	 * @param _token The asset being borrowed.
	 * @param _amount The borrowed amount.
	 * @param _fee The fees to be paid along with the borrowed amount.
	 * @param _params Additional encoded parameters to be decoded.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _receiveFlashLoan(Self storage _self, address _token, uint256 _amount, uint256 _fee, bytes memory _params) external returns (bool _success)
	{
		assert(_token == _self.underlyingToken);
		uint256 _which = abi.decode(_params, (uint256));
		return _self._continueAdjustLeverage(_amount, _fee, _which);
	}

	/**
	 * @dev Converts a given amount of the mining token to the underlying
	 *      token using the external exchange contract. Both amounts are
	 *      deducted and credited, respectively, from the current contract.
	 * @param _inputAmount The amount to be converted.
	 */
	function _convertMiningToUnderlying(Self storage _self, uint256 _inputAmount) internal
	{
		G.dynamicConvertFunds(_self.exchange, _self.miningToken, _self.underlyingToken, _inputAmount, 0);
	}
}

// File: contracts/GFlashBorrower.sol

pragma solidity ^0.6.0;







/**
 * @dev This abstract contract provides an uniform interface for receiving
 *      flash loans. It encapsulates the required functionality provided by
 *      both Aave and Dydx. It performs the basic validation to ensure that
 *      only Aave/Dydx contracts can dispatch the operation and only the
 *      current contract (that inherits from it) can initiate it.
 */
abstract contract GFlashBorrower is FlashLoanReceiver, ICallee
{
	using SafeMath for uint256;

	uint256 private allowOperationLevel = 0;

	/**
	 * @dev Handy definition to ensure that flash loans are only initiated
	 *      from within the current contract.
	 */
	modifier mayFlashBorrow()
	{
		allowOperationLevel++;
		_;
		allowOperationLevel--;
	}

	/**
	 * @dev Handles Aave callback. Delegates the processing of the funds
	 *      to the virtual function _processFlashLoan and later takes care
	 *      of paying it back.
	 * @param _token The ERC-20 contract address.
	 * @param _amount The amount being borrowed.
	 * @param _fee The fee, in addition to the amount borrowed, to be repaid.
	 * @param _params Additional user parameters provided when the flash
	 *                loan was requested.
	 */
	function executeOperation(address _token, uint256 _amount, uint256 _fee, bytes calldata _params) external override
	{
		assert(allowOperationLevel > 0);
		address _from = msg.sender;
		address _pool = $.Aave_AAVE_LENDING_POOL;
		assert(_from == _pool);
		require(_processFlashLoan(_token, _amount, _fee, _params)/*, "failure processing flash loan"*/);
		G.paybackFlashLoan(FlashLoans.Provider.Aave, _token, _amount.add(_fee));
	}

	/**
	 * @dev Handles Dydx callback. Delegates the processing of the funds
	 *      to the virtual function _processFlashLoan and later takes care
	 *      of paying it back.
	 * @param _sender The contract address of the initiator of the flash
	 *                loan, expected to be the current contract.
	 * @param _account Dydx account info provided in the callback.
	 * @param _data Aditional external data provided to the Dydx callback,
	 *              this is used by the Dydx module to pass the ERC-20 token
	 *              address, the amount and fee, as well as user parameters.
	 */
	function callFunction(address _sender, Account.Info memory _account, bytes memory _data) external override
	{
		assert(allowOperationLevel > 0);
		address _from = msg.sender;
		address _solo = $.Dydx_SOLO_MARGIN;
		assert(_from == _solo);
		assert(_sender == address(this));
		assert(_account.owner == address(this));
		(address _token, uint256 _amount, uint256 _fee, bytes memory _params) = abi.decode(_data, (address,uint256,uint256,bytes));
		require(_processFlashLoan(_token, _amount, _fee, _params)/*, "failure processing flash loan"*/);
		G.paybackFlashLoan(FlashLoans.Provider.Dydx, _token, _amount.add(_fee));
	}

	/**
	 * @dev Internal function that abstracts the algorithm to be performed
	 *      with borrowed funds. It receives the funds, deposited in the
	 *      current contract, and must ensure they are available as balance
	 *      of the current contract, including fees, before it returns.
	 * @param _token The ERC-20 contract address.
	 * @param _amount The amount being borrowed.
	 * @param _fee The fee, in addition to the amount borrowed, to be repaid.
	 * @param _params Additional user parameters provided when the flash
	 *                loan was requested.
	 * @return _success A boolean indicating success.
	 */
	function _processFlashLoan(address _token, uint256 _amount, uint256 _fee, bytes memory _params) internal virtual returns (bool _success);
}

// File: contracts/GCTokenType1.sol

pragma solidity ^0.6.0;







/**
 * @notice This contract implements the functionality for the gcToken Type 1.
 *         As with all gcTokens, gcTokens Type 1 use a Compound cToken as
 *         reserve token. Furthermore, Type 1 tokens may apply leverage to the
 *         reserve by using the cToken balance to borrow its associated
 *         underlying asset which in turn is used to mint more cToken. This
 *         process is performed to the limit where the actual reserve balance
 *         ends up accounting for the difference between the total amount lent
 *         and the total amount borrowed. One may observe that there is
 *         always a net loss when considering just the yield accrued for
 *         lending minus the yield accrued for borrowing on Compound. However,
 *         if we consider COMP being credited for liquidity mining the net
 *         balance may become positive and that is when the leverage mechanism
 *         should be applied. The COMP is periodically converted to the
 *         underlying asset and naturally becomes part of the reserve.
 *         In order to easily and efficiently adjust the leverage, this contract
 *         performs flash loans. See GCTokenBase, GFlashBorrower and
 *         GCLeveragedReserveManager for further documentation.
 */
contract GCTokenType1 is GCTokenBase, GFlashBorrower
{
	using GCLeveragedReserveManager for GCLeveragedReserveManager.Self;

	GCLeveragedReserveManager.Self lrm;

	/**
	 * @dev Constructor for the gcToken Type 1 contract.
	 * @param _name The ERC-20 token name.
	 * @param _symbol The ERC-20 token symbol.
	 * @param _decimals The ERC-20 token decimals.
	 * @param _stakesToken The ERC-20 token address to be used as stakes
	 *                     token (GRO).
	 * @param _reserveToken The ERC-20 token address to be used as reserve
	 *                      token (e.g. cDAI for gcDAI).
	 * @param _miningToken The ERC-20 token used for liquidity mining on
	 *                     compound (COMP).
	 */
	constructor (string memory _name, string memory _symbol, uint8 _decimals, address _stakesToken, address _reserveToken, address _miningToken)
		GCTokenBase(_name, _symbol, _decimals, _stakesToken, _reserveToken, _miningToken, address(0)) public
	{
		lrm.init(_reserveToken, _miningToken);
	}

	/**
	 * @notice Overrides the default total reserve definition in order to
	 *         account only for the diference between assets being lent
	 *         and assets being borrowed.
	 * @return _totalReserve The amount of the reserve token corresponding
	 *                       to this contract's worth.
	 */
	function totalReserve() public view override returns (uint256 _totalReserve)
	{
		return GCFormulae._calcCostFromUnderlyingCost(totalReserveUnderlying(), exchangeRate());
	}

	/**
	 * @notice Overrides the default total underlying reserve definition in
	 *         order to account only for the diference between assets being
	 *         lent and assets being borrowed.
	 * @return _totalReserveUnderlying The amount of the underlying asset
	 *                                 corresponding to this contract's worth.
	 */
	function totalReserveUnderlying() public view override returns (uint256 _totalReserveUnderlying)
	{
		return lendingReserveUnderlying().sub(borrowingReserveUnderlying());
	}

	/**
	 * @notice Provides the contract address for the GExchange implementation
	 *         currently being used to convert the mining token (COMP) into
	 *         the underlying asset.
	 * @return _exchange A GExchange compatible contract address, or address(0)
	 *                   if it has not been set.
	 */
	function exchange() public view override returns (address _exchange)
	{
		return lrm.exchange;
	}

	/**
	 * @notice Provides the minimum and maximum amount of the mining token to
	 *         be processed on every operation. If the contract balance
	 *         is below the minimum it waits until more accumulates.
	 *         If the total amount is beyond the maximum it processes the
	 *         maximum and leaves the rest for future operations. The mining
	 *         token accumulated via liquidity mining is converted to the
	 *         underlying asset and used to mint the associated cToken.
	 *         This range is used to avoid wasting gas converting small
	 *         amounts as well as mitigating slipage converting large amounts.
	 * @return _miningMinGulpAmount The minimum amount of the mining token
	 *                              to be processed per deposit/withdrawal.
	 * @return _miningMaxGulpAmount The maximum amount of the mining token
	 *                              to be processed per deposit/withdrawal.
	 */
	function miningGulpRange() public view override returns (uint256 _miningMinGulpAmount, uint256 _miningMaxGulpAmount)
	{
		return (lrm.miningMinGulpAmount, lrm.miningMaxGulpAmount);
	}

	/**
	 * @notice Provides the minimum and maximum amount of the gcToken Type 1 to
	 *         be processed on every operation. This method applies only to
	 *         gcTokens Type 2 and is not relevant for gcTokens Type 1.
	 * @return _growthMinGulpAmount The minimum amount of the gcToken Type 1
	 *                              to be processed per deposit/withdrawal
	 *                              (always 0).
	 * @return _growthMaxGulpAmount The maximum amount of the gcToken Type 1
	 *                              to be processed per deposit/withdrawal
	 *                              (always 0).
	 */
	function growthGulpRange() public view override returns (uint256 _growthMinGulpAmount, uint256 _growthMaxGulpAmount)
	{
		return (0, 0);
	}

	/**
	 * @notice Provides the target collateralization ratio and margin to be
	 *         maintained by this contract. The amount is relative to the
	 *         maximum collateralization available for the associated cToken
	 *         on Compound. gcToken Type 1 uses leveraged collateralization
	 *         where the cToken is used to borrow its underlying token which
	 *         in turn is used to mint new cToken and repeat. This is
	 *         performed to the maximal level where the actual reserve
	 *         ends up corresponding to the difference between the amount
	 *         lent and the amount borrowed.
	 * @param _collateralizationRatio The percent value relative to the
	 *                                maximum allowed that this contract
	 *                                will target for collateralization
	 *                                (defaults to 94%)
	 * @param _collateralizationRatio The percent value relative to the
	 *                                maximum allowed that this contract
	 *                                will target for collateralization
	 *                                margin (defaults to 2%)
	 */
	function collateralizationRatio() public view override returns (uint256 _collateralizationRatio, uint256 _collateralizationMargin)
	{
		return (lrm.collateralizationRatio, lrm.collateralizationMargin);
	}

	/**
	 * @notice Sets the contract address for the GExchange implementation
	 *         to be used in converting the mining token (COMP) into
	 *         the underlying asset. This is a priviledged function
	 *         restricted to the contract owner.
	 * @param _exchange A GExchange compatible contract address.
	 */
	function setExchange(address _exchange) public override onlyOwner nonReentrant
	{
		lrm.setExchange(_exchange);
	}

	/**
	 * @notice Sets the minimum and maximum amount of the mining token to
	 *         be processed on every operation. See miningGulpRange().
	 *         This is a priviledged function restricted to the contract owner.
	 * @param _miningMinGulpAmount The minimum amount of the mining token
	 *                             to be processed per deposit/withdrawal.
	 * @param _miningMaxGulpAmount The maximum amount of the mining token
	 *                             to be processed per deposit/withdrawal.
	 */
	function setMiningGulpRange(uint256 _miningMinGulpAmount, uint256 _miningMaxGulpAmount) public override onlyOwner nonReentrant
	{
		lrm.setMiningGulpRange(_miningMinGulpAmount, _miningMaxGulpAmount);
	}

	/**
	 * @notice Sets the minimum and maximum amount of the gcToken Type 1 to
	 *         be processed on every operation. This method applies only to
	 *         gcTokens Type 2 and is not relevant for gcTokens Type 1.
	 *         This is a priviledged function restricted to the contract owner.
	 * @param _growthMinGulpAmount The minimum amount of the gcToken Type 1
	 *                             to be processed per deposit/withdrawal
	 *                             (ignored).
	 * @param _growthMaxGulpAmount The maximum amount of the gcToken Type 1
	 *                             to be processed per deposit/withdrawal
	 *                             (ignored).
	 */
	function setGrowthGulpRange(uint256 _growthMinGulpAmount, uint256 _growthMaxGulpAmount) public override /*onlyOwner nonReentrant*/
	{
		_growthMinGulpAmount; _growthMaxGulpAmount; // silences warnings
	}

	/**
	 * @notice Sets the target collateralization ratio and margin to be
	 *         maintained by this contract. See collateralizationRatio().
	 *         Setting both parameters to 0 turns off collateralization and
	 *         leveraging. This is a priviledged function restricted to the
	 *         contract owner.
	 * @param _collateralizationRatio The percent value relative to the
	 *                                maximum allowed that this contract
	 *                                will target for collateralization
	 *                                (defaults to 94%)
	 * @param _collateralizationRatio The percent value relative to the
	 *                                maximum allowed that this contract
	 *                                will target for collateralization
	 *                                margin (defaults to 2%)
	 */
	function setCollateralizationRatio(uint256 _collateralizationRatio, uint256 _collateralizationMargin) public override onlyOwner nonReentrant
	{
		lrm.setCollateralizationRatio(_collateralizationRatio, _collateralizationMargin);
	}

	/**
	 * @dev This method is overriden from GTokenBase and sets up the reserve
	 *      after a deposit comes along. It basically adjusts the
	 *      collateralization/leverage to reflect the new increased reserve
	 *      balance. This method uses the GCLeveragedReserveManager to
	 *      adjust the reserve and this is done via flash loans.
	 *      See GCLeveragedReserveManager.sol.
	 * @param _cost The amount of reserve being deposited (ignored).
	 * @return _success A boolean indicating whether or not the operation
	 *                  succeeded. This operation should not fail unless
	 *                  any of the underlying components (Compound, Aave,
	 *                  Dydx) also fails.
	 */
	function _prepareDeposit(uint256 _cost) internal override mayFlashBorrow returns (bool _success)
	{
		_cost; // silences warnings
		return lrm.adjustReserve(0);
	}

	/**
	 * @dev This method is overriden from GTokenBase and sets up the reserve
	 *      before a withdrawal comes along. It basically calculates the
	 *      the amount that will be left in the reserve, in terms of cToken
	 *      cost, and adjusts the collateralization/leverage accordingly. This
	 *      method uses the GCLeveragedReserveManager to adjust the reserve
	 *      and this is done via flash loans. See GCLeveragedReserveManager.sol.
	 * @param _cost The amount of reserve being withdrawn and that needs to
	 *              be immediately liquid.
	 * @return _success A boolean indicating whether or not the operation succeeded.
	 *                  The operation may fail if it is not possible to recover
	 *                  the required liquidity (e.g. low liquidity in the markets).
	 */
	function _prepareWithdrawal(uint256 _cost) internal override mayFlashBorrow returns (bool _success)
	{
		return lrm.adjustReserve(GCFormulae._calcUnderlyingCostFromCost(_cost, GC.fetchExchangeRate(reserveToken)));
	}

	/**
	 * @dev This method dispatches the flash loan callback back to the
	 *      GCLeveragedReserveManager library. See GCLeveragedReserveManager.sol
	 *      and GFlashBorrower.sol.
	 */
	function _processFlashLoan(address _token, uint256 _amount, uint256 _fee, bytes memory _params) internal override returns (bool _success)
	{
		return lrm._receiveFlashLoan(_token, _amount, _fee, _params);
	}
}

// File: contracts/GCDelegatedReserveManager.sol

pragma solidity ^0.6.0;





/**
 * @dev This library implements data structure abstraction for the delegated
 *      reserve management code in order to circuvent the EVM contract size limit.
 *      It is therefore a public library shared by all gcToken Type 2 contracts and
 *      needs to be published alongside them. See GCTokenType2.sol for further
 *      documentation.
 */
library GCDelegatedReserveManager
{
	using SafeMath for uint256;
	using GCDelegatedReserveManager for GCDelegatedReserveManager.Self;

	uint256 constant MAXIMUM_COLLATERALIZATION_RATIO = 96e16; // 96% of 50% = 48%
	uint256 constant DEFAULT_COLLATERALIZATION_RATIO = 66e16; // 66% of 50% = 33%
	uint256 constant DEFAULT_COLLATERALIZATION_MARGIN = 8e16; // 8% of 50% = 4%

	struct Self {
		address reserveToken;
		address underlyingToken;

		address exchange;

		address miningToken;
		uint256 miningMinGulpAmount;
		uint256 miningMaxGulpAmount;

		address borrowToken;

		address growthToken;
		address growthReserveToken;
		uint256 growthMinGulpAmount;
		uint256 growthMaxGulpAmount;

		uint256 collateralizationRatio;
		uint256 collateralizationMargin;
	}

	/**
	 * @dev Initializes the data structure. This method is exposed publicly.
	 *      Note that the underlying borrowing token must match the growth
	 *      reserve token given that funds borrowed will be reinvested in
	 *      the provided growth token (gToken).
	 * @param _reserveToken The ERC-20 token address of the reserve token (cToken).
	 * @param _miningToken The ERC-20 token address to be collected from
	 *                     liquidity mining (COMP).
	 * @param _borrowToken The ERC-20 token address of the borrow token (cToken).
	 * @param _growthToken The ERC-20 token address of the growth token (gToken).
	 */
	function init(Self storage _self, address _reserveToken, address _miningToken, address _borrowToken, address _growthToken) public
	{
		address _underlyingToken = GC.getUnderlyingToken(_reserveToken);
		address _borrowUnderlyingToken = GC.getUnderlyingToken(_borrowToken);
		address _growthReserveToken = GToken(_growthToken).reserveToken();
		assert(_borrowUnderlyingToken == _growthReserveToken);

		_self.reserveToken = _reserveToken;
		_self.underlyingToken = _underlyingToken;

		_self.exchange = address(0);

		_self.miningToken = _miningToken;
		_self.miningMinGulpAmount = 0;
		_self.miningMaxGulpAmount = 0;

		_self.borrowToken = _borrowToken;

		_self.growthToken = _growthToken;
		_self.growthReserveToken = _growthReserveToken;
		_self.growthMinGulpAmount = 0;
		_self.growthMaxGulpAmount = 0;

		_self.collateralizationRatio = DEFAULT_COLLATERALIZATION_RATIO;
		_self.collateralizationMargin = DEFAULT_COLLATERALIZATION_MARGIN;

		GC.safeEnter(_reserveToken);
	}

	/**
	 * @dev Sets the contract address for asset conversion delegation.
	 *      This library converts the miningToken into the underlyingToken
	 *      and use the assets to back the reserveToken. See GExchange.sol
	 *      for further documentation. This method is exposed publicly.
	 * @param _exchange The address of the contract that implements the
	 *                  GExchange interface.
	 */
	function setExchange(Self storage _self, address _exchange) public
	{
		_self.exchange = _exchange;
	}

	/**
	 * @dev Sets the range for converting liquidity mining assets. This
	 *      method is exposed publicly.
	 * @param _miningMinGulpAmount The minimum amount, funds will only be
	 *                             converted once the minimum is accumulated.
	 * @param _miningMaxGulpAmount The maximum amount, funds beyond this
	 *                             limit will not be converted and are left
	 *                             for future rounds of conversion.
	 */
	function setMiningGulpRange(Self storage _self, uint256 _miningMinGulpAmount, uint256 _miningMaxGulpAmount) public
	{
		require(_miningMinGulpAmount <= _miningMaxGulpAmount, "invalid range");
		_self.miningMinGulpAmount = _miningMinGulpAmount;
		_self.miningMaxGulpAmount = _miningMaxGulpAmount;
	}

	/**
	 * @dev Sets the range for converting growth profits. This
	 *      method is exposed publicly.
	 * @param _growthMinGulpAmount The minimum amount, funds will only be
	 *                             converted once the minimum is accumulated.
	 * @param _growthMaxGulpAmount The maximum amount, funds beyond this
	 *                             limit will not be converted and are left
	 *                             for future rounds of conversion.
	 */
	function setGrowthGulpRange(Self storage _self, uint256 _growthMinGulpAmount, uint256 _growthMaxGulpAmount) public
	{
		require(_growthMinGulpAmount <= _growthMaxGulpAmount, "invalid range");
		_self.growthMinGulpAmount = _growthMinGulpAmount;
		_self.growthMaxGulpAmount = _growthMaxGulpAmount;
	}

	/**
	 * @dev Sets the collateralization ratio and margin. These values are
	 *      percentual and relative to the maximum collateralization ratio
	 *      provided by the underlying asset. This method is exposed publicly.
	 * @param _collateralizationRatio The target collateralization ratio,
	 *                                between lend and borrow, that the
	 *                                reserve will try to maintain.
	 * @param _collateralizationMargin The deviation from the target ratio
	 *                                 that should be accepted.
	 */
	function setCollateralizationRatio(Self storage _self, uint256 _collateralizationRatio, uint256 _collateralizationMargin) public
	{
		require(_collateralizationMargin <= _collateralizationRatio && _collateralizationRatio.add(_collateralizationMargin) <= MAXIMUM_COLLATERALIZATION_RATIO, "invalid ratio");
		_self.collateralizationRatio = _collateralizationRatio;
		_self.collateralizationMargin = _collateralizationMargin;
	}

	/**
	 * @dev Performs the reserve adjustment actions leaving a liquidity room,
	 *      if necessary. It will attempt to incorporate the liquidity mining
	 *      assets into the reserve, the profits from the underlying growth
	 *      investment and adjust the collateralization targeting the
	 *      configured ratio. This method is exposed publicly.
	 * @param _roomAmount The underlying token amount to be available after the
	 *                    operation. This is revelant for withdrawals, once the
	 *                    room amount is withdrawn the reserve should reflect
	 *                    the configured collateralization ratio.
	 * @return _success A boolean indicating whether or not both actions suceeded.
	 */
	function adjustReserve(Self storage _self, uint256 _roomAmount) public returns (bool _success)
	{
		bool _success1 = _self._gulpMiningAssets();
		bool _success2 = _self._gulpGrowthAssets();
		bool _success3 = _self._adjustReserve(_roomAmount);
		return _success1 && _success2 && _success3;
	}

	/**
	 * @dev Calculates the collateralization ratio relative to the maximum
	 *      collateralization ratio provided by the underlying asset.
	 * @return _collateralizationRatio The target absolute collateralization ratio.
	 */
	function _calcCollateralizationRatio(Self storage _self) internal view returns (uint256 _collateralizationRatio)
	{
		return GC.getCollateralRatio(_self.reserveToken).mul(_self.collateralizationRatio).div(1e18);
	}

	/**
	 * @dev Incorporates the liquidity mining assets into the reserve. Assets
	 *      are converted to the underlying asset and then added to the reserve.
	 *      If the amount available is below the minimum, or if the exchange
	 *      contract is not set, nothing is done. Otherwise the operation is
	 *      performed, limited to the maximum amount. Note that this operation
	 *      will incorporate to the reserve all the underlying token balance
	 *      including funds sent to it or left over somehow.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _gulpMiningAssets(Self storage _self) internal returns (bool _success)
	{
		if (_self.exchange == address(0)) return true;
		if (_self.miningMaxGulpAmount == 0) return true;
		uint256 _miningAmount = G.getBalance(_self.miningToken);
		if (_miningAmount == 0) return true;
		if (_miningAmount < _self.miningMinGulpAmount) return true;
		_self._convertMiningToUnderlying(G.min(_miningAmount, _self.miningMaxGulpAmount));
		return GC.lend(_self.reserveToken, G.getBalance(_self.underlyingToken));
	}

	/**
	 * @dev Incorporates the profits from growth into the reserve. Assets
	 *      are converted to the underlying asset and then added to the reserve.
	 *      If the amount available is below the minimum, or if the exchange
	 *      contract is not set, nothing is done. Otherwise the operation is
	 *      performed, limited to the maximum amount. Note that this operation
	 *      will incorporate to the reserve all the growth reserve token balance
	 *      including funds sent to it or left over somehow.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _gulpGrowthAssets(Self storage _self) internal returns (bool _success)
	{
		if (_self.exchange == address(0)) return true;
		if (_self.growthMaxGulpAmount == 0) return true;
		// calculates how much was borrowed
		uint256 _borrowAmount = GC.fetchBorrowAmount(_self.borrowToken);
		// calculates how much can be redeemed from the growth token
		uint256 _totalShares = G.getBalance(_self.growthToken);
		uint256 _redeemableAmount = _self._calcWithdrawalCostFromShares(_totalShares);
		// if there is a profit and that amount is within range
		// it gets converted to the underlying reserve token and
		// incorporated to the reserve
		if (_redeemableAmount <= _borrowAmount) return true;
		uint256 _growthAmount = _redeemableAmount.sub(_borrowAmount);
		if (_growthAmount < _self.growthMinGulpAmount) return true;
		uint256 _grossShares = _self._calcWithdrawalSharesFromCost(G.min(_growthAmount, _self.growthMaxGulpAmount));
		_grossShares = G.min(_grossShares, _totalShares);
		if (_grossShares == 0) return true;
		_success = _self._withdraw(_grossShares);
		if (!_success) return false;
		_self._convertGrowthReserveToUnderlying(G.getBalance(_self.growthReserveToken));
		return GC.lend(_self.reserveToken, G.getBalance(_self.underlyingToken));
	}

	/**
	 * @dev Adjusts the reserve to match the configured collateralization
	 *      ratio. It uses the reserve collateral to borrow a proper amount
	 *      of the growth token reserve asset and deposit it. Or it
	 *      redeems from the growth token and repays the loan.
	 * @param _roomAmount The amount of underlying token to be liquid after
	 *                    the operation.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _adjustReserve(Self storage _self, uint256 _roomAmount) internal returns (bool _success)
	{
		// calculates the percental change from the current reserve
		// and the reserve deducting the room amount
		uint256 _scalingRatio;
		{
			uint256 _reserveAmount = GC.fetchLendAmount(_self.reserveToken);
			_roomAmount = G.min(_roomAmount, _reserveAmount);
			uint256 _newReserveAmount = _reserveAmount.sub(_roomAmount);
			_scalingRatio = _reserveAmount > 0 ? uint256(1e18).mul(_newReserveAmount).div(_reserveAmount) : 0;
		}
		// calculates the borrowed amount and range in terms of the reserve token
		uint256 _borrowAmount = GC.fetchBorrowAmount(_self.borrowToken);
		uint256 _newBorrowAmount;
		uint256 _minBorrowAmount;
		uint256 _maxBorrowAmount;
		{
			uint256 _freeAmount = GC.getLiquidityAmount(_self.borrowToken);
			uint256 _totalAmount = _borrowAmount.add(_freeAmount);
			// applies the scaling ratio to account for the required room
			uint256 _newTotalAmount = _totalAmount.mul(_scalingRatio).div(1e18);
			_newBorrowAmount = _newTotalAmount.mul(_self.collateralizationRatio).div(1e18);
			uint256 _newMarginAmount = _newTotalAmount.mul(_self.collateralizationMargin).div(1e18);
			_minBorrowAmount = _newBorrowAmount.sub(G.min(_newMarginAmount, _newBorrowAmount));
			_maxBorrowAmount = G.min(_newBorrowAmount.add(_newMarginAmount), _newTotalAmount);
		}
		// if the borrow amount is below the lower bound,
		// borrows the diference and deposits in the growth token contract
		if (_borrowAmount < _minBorrowAmount) {
			uint256 _amount = _newBorrowAmount.sub(_borrowAmount);
			_amount = G.min(_amount, GC.getMarketAmount(_self.borrowToken));
			_success = GC.borrow(_self.borrowToken, _amount);
			if (!_success) return false;
			_success = _self._deposit(_amount);
			if (_success) return true;
			GC.repay(_self.borrowToken, _amount);
			return false;
		}
		// if the borrow amount is above the upper bound,
		// redeems the diference from the growth token contract and
		// repays the loan
		if (_borrowAmount > _maxBorrowAmount) {
			uint256 _amount = _borrowAmount.sub(_newBorrowAmount);
			uint256 _grossShares = _self._calcWithdrawalSharesFromCost(_amount);
			_grossShares = G.min(_grossShares, G.getBalance(_self.growthToken));
			if (_grossShares == 0) return true;
			_success = _self._withdraw(_grossShares);
			if (!_success) return false;
			uint256 _repayAmount = G.min(_borrowAmount, G.getBalance(_self.growthReserveToken));
			return GC.repay(_self.borrowToken, _repayAmount);
		}
		return true;
	}

	/**
	 * @dev Calculates how much of the growth reserve token can be redeemed
	 *      from a given amount of shares.
	 * @param _grossShares The number of shares to redeem.
	 * @return _cost The reserve token amount to be withdraw.
	 */
	function _calcWithdrawalCostFromShares(Self storage _self, uint256 _grossShares) internal view returns (uint256 _cost) {
		uint256 _totalReserve = GToken(_self.growthToken).totalReserve();
		uint256 _totalSupply = GToken(_self.growthToken).totalSupply();
		uint256 _withdrawalFee = GToken(_self.growthToken).withdrawalFee();
		(_cost,) = GToken(_self.growthToken).calcWithdrawalCostFromShares(_grossShares, _totalReserve, _totalSupply, _withdrawalFee);
		return _cost;
	}

	/**
	 * @dev Calculates how many shares must be redeemed in order to withdraw
	 *      so much of the growth reserve token.
	 * @param _cost The amount of the reserve token to be received on
	 *               withdrawal.
	 * @return _grossShares The number of shares one must redeem.
	 */
	function _calcWithdrawalSharesFromCost(Self storage _self, uint256 _cost) internal view returns (uint256 _grossShares) {
		uint256 _totalReserve = GToken(_self.growthToken).totalReserve();
		uint256 _totalSupply = GToken(_self.growthToken).totalSupply();
		uint256 _withdrawalFee = GToken(_self.growthToken).withdrawalFee();
		(_grossShares,) = GToken(_self.growthToken).calcWithdrawalSharesFromCost(_cost, _totalReserve, _totalSupply, _withdrawalFee);
		return _grossShares;
	}

	/**
	 * @dev Deposits into the growth token contract.
	 * @param _cost The amount of thr growth reserve token to be deposited.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _deposit(Self storage _self, uint256 _cost) internal returns (bool _success)
	{
		G.approveFunds(_self.growthReserveToken, _self.growthToken, _cost);
		try GToken(_self.growthToken).deposit(_cost) {
			return true;
		} catch (bytes memory /* _data */) {
			G.approveFunds(_self.growthReserveToken, _self.growthToken, 0);
			return false;
		}
	}

	/**
	 * @dev Withdraws from the growth token contract.
	 * @param _grossShares The number of shares to be redeemed.
	 * @return _success A boolean indicating whether or not the action succeeded.
	 */
	function _withdraw(Self storage _self, uint256 _grossShares) internal returns (bool _success)
	{
		try GToken(_self.growthToken).withdraw(_grossShares) {
			return true;
		} catch (bytes memory /* _data */) {
			return false;
		}
	}

	/**
	 * @dev Converts a given amount of the mining token to the underlying
	 *      token using the external exchange contract. Both amounts are
	 *      deducted and credited, respectively, from the current contract.
	 * @param _inputAmount The amount to be converted.
	 */
	function _convertMiningToUnderlying(Self storage _self, uint256 _inputAmount) internal
	{
		G.dynamicConvertFunds(_self.exchange, _self.miningToken, _self.underlyingToken, _inputAmount, 0);
	}

	/**
	 * @dev Converts a given amount of the growth reserve token to the
	 *      underlying token using the external exchange contract. Both
	 *      amounts are deducted and credited, respectively, from the
	 *      current contract.
	 * @param _inputAmount The amount to be converted.
	 */
	function _convertGrowthReserveToUnderlying(Self storage _self, uint256 _inputAmount) internal
	{
		G.dynamicConvertFunds(_self.exchange, _self.growthReserveToken, _self.underlyingToken, _inputAmount, 0);
	}
}

// File: contracts/GCTokenType2.sol

pragma solidity ^0.6.0;






/**
 * @notice This contract implements the functionality for the gcToken Type 2.
 *         As with all gcTokens, gcTokens Type 2 use a Compound cToken as
 *         reserve token. Furthermore, Type 2 tokens will use that cToken
 *         balance to borrow funds that are then deposited into another gToken.
 *         Periodically the gcToken Type 2 will collect profits from liquidity
 *         mining COMP, as well as profits from investing borrowed assets in
 *         the gToken. These profits are converted into the cToken underlying 
 *         asset and incorporated to the reserve. See GCTokenBase and
 *         GCDelegatedReserveManager for further documentation.
 */
contract GCTokenType2 is GCTokenBase
{
	using GCDelegatedReserveManager for GCDelegatedReserveManager.Self;

	GCDelegatedReserveManager.Self drm;

	/**
	 * @dev Constructor for the gcToken Type 2 contract.
	 * @param _name The ERC-20 token name.
	 * @param _symbol The ERC-20 token symbol.
	 * @param _decimals The ERC-20 token decimals.
	 * @param _stakesToken The ERC-20 token address to be used as stakes
	 *                     token (GRO).
	 * @param _reserveToken The ERC-20 token address to be used as reserve
	 *                      token (e.g. cDAI for gcDAI).
	 * @param _miningToken The ERC-20 token used for liquidity mining on
	 *                     compound (COMP).
	 * @param _borrowToken The cToken used for borrowing funds on compound (cDAI).
	 * @param _growthToken The gToken used for reinvesting borrowed funds (gDAI).
	 */
	constructor (string memory _name, string memory _symbol, uint8 _decimals, address _stakesToken, address _reserveToken, address _miningToken, address _borrowToken, address _growthToken)
		GCTokenBase(_name, _symbol, _decimals, _stakesToken, _reserveToken, _miningToken, _growthToken) public
	{
		drm.init(_reserveToken, _miningToken, _borrowToken, _growthToken);
	}

	/**
	 * @notice Provides the total amount of the underlying asset (or equivalent)
	 *         this contract is currently borrowing on Compound.
	 * @return _borrowingReserveUnderlying The underlying asset borrowing
	 *                                     balance on Compound.
	 */
	function borrowingReserveUnderlying() public view override returns (uint256 _borrowingReserveUnderlying)
	{
		uint256 _lendAmount = GC.getLendAmount(reserveToken);
		uint256 _availableAmount = _lendAmount.mul(GC.getCollateralRatio(reserveToken)).div(1e18);
		uint256 _borrowAmount = GC.getBorrowAmount(drm.borrowToken);
		uint256 _freeAmount = GC.getLiquidityAmount(drm.borrowToken);
		uint256 _totalAmount = _borrowAmount.add(_freeAmount);
		return _totalAmount > 0 ? _availableAmount.mul(_borrowAmount).div(_totalAmount) : 0;
	}

	/**
	 * @notice Provides the contract address for the GExchange implementation
	 *         currently being used to convert the mining token (COMP), and
	 *         the gToken reserve token (DAI), into the underlying asset.
	 * @return _exchange A GExchange compatible contract address, or address(0)
	 *                   if it has not been set.
	 */
	function exchange() public view override returns (address _exchange)
	{
		return drm.exchange;
	}

	/**
	 * @notice Provides the minimum and maximum amount of the mining token to
	 *         be processed on every operation. If the contract balance
	 *         is below the minimum it waits until more accumulates.
	 *         If the total amount is beyond the maximum it processes the
	 *         maximum and leaves the rest for future operations. The mining
	 *         token accumulated via liquidity mining is converted to the
	 *         underlying asset and used to mint the associated cToken.
	 *         This range is used to avoid wasting gas converting small
	 *         amounts as well as mitigating slipage converting large amounts.
	 * @return _miningMinGulpAmount The minimum amount of the mining token
	 *                              to be processed per deposit/withdrawal.
	 * @return _miningMaxGulpAmount The maximum amount of the mining token
	 *                              to be processed per deposit/withdrawal.
	 */
	function miningGulpRange() public view override returns (uint256 _miningMinGulpAmount, uint256 _miningMaxGulpAmount)
	{
		return (drm.miningMinGulpAmount, drm.miningMaxGulpAmount);
	}

	/**
	 * @notice Provides the minimum and maximum amount of the gToken reserve
	 *         profit to be processed on every operation. If the profit balance
	 *         is below the minimum it waits until more accumulates.
	 *         If the total profit is beyond the maximum it processes the
	 *         maximum and leaves the rest for future operations. The profit
	 *         accumulated via gToken reinvestment is converted to the
	 *         underlying asset and used to mint the associated cToken.
	 *         This range is used to avoid wasting gas converting small
	 *         amounts as well as mitigating slipage converting large amounts.
	 * @return _growthMinGulpAmount The minimum profit of the gToken reserve
	 *                              to be processed per deposit/withdrawal.
	 * @return _growthMaxGulpAmount The maximum profit of the gToken reserve
	 *                              to be processed per deposit/withdrawal.
	 */
	function growthGulpRange() public view override returns (uint256 _growthMinGulpAmount, uint256 _growthMaxGulpAmount)
	{
		return (drm.growthMinGulpAmount, drm.growthMaxGulpAmount);
	}

	/**
	 * @notice Provides the target collateralization ratio and margin to be
	 *         maintained by this contract. The amount is relative to the
	 *         maximum collateralization available for the associated cToken
	 *         on Compound. gcToken Type 2 uses the reserve token as collateral
	 *         to borrow funds and revinvest into the gToken.
	 * @param _collateralizationRatio The percent value relative to the
	 *                                maximum allowed that this contract
	 *                                will target for collateralization
	 *                                (defaults to 66%)
	 * @param _collateralizationRatio The percent value relative to the
	 *                                maximum allowed that this contract
	 *                                will target for collateralization
	 *                                margin (defaults to 8%)
	 */
	function collateralizationRatio() public view override returns (uint256 _collateralizationRatio, uint256 _collateralizationMargin)
	{
		return (drm.collateralizationRatio, drm.collateralizationMargin);
	}

	/**
	 * @notice Sets the contract address for the GExchange implementation
	 *         to be used in converting the mining token (COMP), and
	 *         the gToken reserve token (DAI), into the underlying asset.
	 *         This is a priviledged function restricted to the contract owner.
	 * @param _exchange A GExchange compatible contract address.
	 */
	function setExchange(address _exchange) public override onlyOwner nonReentrant
	{
		drm.setExchange(_exchange);
	}

	/**
	 * @notice Sets the minimum and maximum amount of the mining token to
	 *         be processed on every operation. See miningGulpRange().
	 *         This is a priviledged function restricted to the contract owner.
	 * @param _miningMinGulpAmount The minimum amount of the mining token
	 *                             to be processed per deposit/withdrawal.
	 * @param _miningMaxGulpAmount The maximum amount of the mining token
	 *                             to be processed per deposit/withdrawal.
	 */
	function setMiningGulpRange(uint256 _miningMinGulpAmount, uint256 _miningMaxGulpAmount) public override onlyOwner nonReentrant
	{
		drm.setMiningGulpRange(_miningMinGulpAmount, _miningMaxGulpAmount);
	}

	/**
	 * @notice Sets the minimum and maximum amount of the gToken reserve profit
	 *         to be processed on every operation. See growthGulpRange().
	 *         This is a priviledged function restricted to the contract owner.
	 * @param _growthMinGulpAmount The minimum profit of the gToken reserve
	 *                             to be processed per deposit/withdrawal.
	 * @param _growthMaxGulpAmount The maximum profit of the gToken reserve
	 *                             to be processed per deposit/withdrawal.
	 */
	function setGrowthGulpRange(uint256 _growthMinGulpAmount, uint256 _growthMaxGulpAmount) public override onlyOwner nonReentrant
	{
		drm.setGrowthGulpRange(_growthMinGulpAmount, _growthMaxGulpAmount);
	}

	/**
	 * @notice Sets the target collateralization ratio and margin to be
	 *         maintained by this contract. See collateralizationRatio().
	 *         Setting both parameters to 0 turns off collateralization.
	 *         This is a priviledged function restricted to the contract owner.
	 * @param _collateralizationRatio The percent value relative to the
	 *                                maximum allowed that this contract
	 *                                will target for collateralization
	 *                                (defaults to 66%)
	 * @param _collateralizationRatio The percent value relative to the
	 *                                maximum allowed that this contract
	 *                                will target for collateralization
	 *                                margin (defaults to 8%)
	 */
	function setCollateralizationRatio(uint256 _collateralizationRatio, uint256 _collateralizationMargin) public override onlyOwner nonReentrant
	{
		drm.setCollateralizationRatio(_collateralizationRatio, _collateralizationMargin);
	}

	/**
	 * @dev This method is overriden from GTokenBase and sets up the reserve
	 *      after a deposit comes along. It basically adjusts the
	 *      collateralization to reflect the new increased reserve
	 *      balance. This method uses the GCDelegatedReserveManager to
	 *      adjust the reserve. See GCDelegatedReserveManager.sol.
	 * @param _cost The amount of reserve being deposited (ignored).
	 * @return _success A boolean indicating whether or not the operation
	 *                  succeeded.
	 */
	function _prepareDeposit(uint256 _cost) internal override returns (bool _success)
	{
		_cost; // silences warnings
		return drm.adjustReserve(0);
	}

	/**
	 * @dev This method is overriden from GTokenBase and sets up the reserve
	 *      before a withdrawal comes along. It basically calculates the
	 *      the amount that will be left in the reserve, in terms of cToken
	 *      cost, and adjusts the collateralization accordingly. This
	 *      method uses the GCDelegatedReserveManager to adjust the reserve.
	 *      See GCDelegatedReserveManager.sol.
	 * @param _cost The amount of reserve being withdrawn and that needs to
	 *              be immediately liquid.
	 * @return _success A boolean indicating whether or not the operation succeeded.
	 *                  The operation may fail if it is not possible to recover
	 *                  the required liquidity (e.g. low liquidity in the markets).
	 */
	function _prepareWithdrawal(uint256 _cost) internal override returns (bool _success)
	{
		return drm.adjustReserve(GCFormulae._calcUnderlyingCostFromCost(_cost, GC.fetchExchangeRate(reserveToken)));
	}
}

// File: contracts/GEtherBridge.sol

pragma solidity ^0.6.0;








/**
 * @dev This contract serves as a useful bridge between ETH and the WETH
 *      ERC-20 based gTokens. It accepts deposits/withdrawals in ETH performing
 *      the wrapping/unwrapping behind the scenes.
 */
contract GEtherBridge
{
	/**
	 * @notice Accepts a deposit to the gToken using ETH. The gToken must
	 *         have WETH as its reserveToken. This is a payable method and
	 *         expects ETH to be sent; which in turn will be converted into
	 *         shares. See GToken.sol and GTokenBase.sol for further
	 *         documentation.
	 * @param _growthToken The WETH based gToken.
	 */
	function deposit(address _growthToken) public payable
	{
		address _from = msg.sender;
		uint256 _cost = msg.value;
		address _reserveToken = GToken(_growthToken).reserveToken();
		require(_reserveToken == $.WETH, "ETH operation not supported by token");
		G.safeWrap(_cost);
		G.approveFunds(_reserveToken, _growthToken, _cost);
		GToken(_growthToken).deposit(_cost);
		uint256 _netShares = G.getBalance(_growthToken);
		G.pushFunds(_growthToken, _from, _netShares);
	}

	/**
	 * @notice Accepts a withdrawal to the gToken using ETH. The gToken must
	 *         have WETH as its reserveToken. This method will redeem the
	 *         sender's required balance in shares; which in turn will receive
	 *         ETH. See GToken.sol and GTokenBase.sol for further documentation.
	 * @param _growthToken The WETH based gToken.
	 * @param _grossShares The number of shares to be redeemed.
	 */
	function withdraw(address _growthToken, uint256 _grossShares) public
	{
		address payable _from = msg.sender;
		address _reserveToken = GToken(_growthToken).reserveToken();
		require(_reserveToken == $.WETH, "ETH operation not supported by token");
		G.pullFunds(_reserveToken, _from, _grossShares);
		GToken(_growthToken).withdraw(_grossShares);
		uint256 _cost = G.getBalance(_reserveToken);
		G.safeUnwrap(_cost);
		_from.transfer(_cost);
	}

	/**
	 * @notice Accepts a deposit to the gcToken using ETH. The gcToken must
	 *         have WETH as its underlyingToken. This is a payable method and
	 *         expects ETH to be sent; which in turn will be converted into
	 *         shares. See GCToken.sol and GCTokenBase.sol for further
	 *         documentation.
	 * @param _growthToken The WETH based gcToken (e.g. gcETH).
	 */
	function depositUnderlying(address _growthToken) public payable
	{
		address _from = msg.sender;
		uint256 _underlyingCost = msg.value;
		address _underlyingToken = GCToken(_growthToken).underlyingToken();
		require(_underlyingToken == $.WETH, "ETH operation not supported by token");
		G.safeWrap(_underlyingCost);
		G.approveFunds(_underlyingToken, _growthToken, _underlyingCost);
		GCToken(_growthToken).depositUnderlying(_underlyingCost);
		uint256 _netShares = G.getBalance(_growthToken);
		G.pushFunds(_growthToken, _from, _netShares);
	}

	/**
	 * @notice Accepts a withdrawal to the gcToken using ETH. The gcToken must
	 *         have WETH as its underlyingToken. This method will redeem the
	 *         sender's required balance in shares; which in turn will receive
	 *         ETH. See GCToken.sol and GCTokenBase.sol for further documentation.
	 * @param _growthToken The WETH based gcToken (e.g. gcETH).
	 * @param _grossShares The number of shares to be redeemed.
	 */
	function withdrawUnderlying(address _growthToken, uint256 _grossShares) public
	{
		address payable _from = msg.sender;
		address _underlyingToken = GCToken(_growthToken).underlyingToken();
		require(_underlyingToken == $.WETH, "ETH operation not supported by token");
		G.pullFunds(_growthToken, _from, _grossShares);
		GCToken(_growthToken).withdrawUnderlying(_grossShares);
		uint256 _underlyingCost = G.getBalance(_underlyingToken);
		G.safeUnwrap(_underlyingCost);
		_from.transfer(_underlyingCost);
	}

	receive() external payable {} // not to be used directly
}

// File: contracts/GTokens.sol

pragma solidity ^0.6.0;





/**
 * @notice Definition of gDAI. As a gToken Type 0, it uses DAI as reserve and
 * distributes to other gToken types.
 */
contract gDAI is GTokenType0
{
	constructor ()
		GTokenType0("growth DAI", "gDAI", 18, $.GRO, $.DAI) public
	{
	}
}

/**
 * @notice Definition of gUSDC. As a gToken Type 0, it uses USDC as reserve and
 * distributes to other gToken types.
 */
contract gUSDC is GTokenType0
{
	constructor ()
		GTokenType0("growth USDC", "gUSDC", 6, $.GRO, $.USDC) public
	{
	}
}

/**
 * @notice Definition of gETH. As a gToken Type 0, it uses WETH as reserve and
 * distributes to other gToken types.
 */
contract gETH is GTokenType0
{
	constructor ()
		GTokenType0("growth ETH", "gETH", 18, $.GRO, $.WETH) public
	{
	}
}

/**
 * @notice Definition of gWBTC. As a gToken Type 0, it uses WBTC as reserve and
 * distributes to other gToken types.
 */
contract gWBTC is GTokenType0
{
	constructor ()
		GTokenType0("growth WBTC", "gWBTC", 8, $.GRO, $.WBTC) public
	{
	}
}

/**
 * @notice Definition of gcDAI. As a gcToken Type 1, it uses cDAI as reserve
 * and employs leverage to maximize returns.
 */
contract gcDAI is GCTokenType1
{
	constructor ()
		GCTokenType1("growth cDAI v2", "gcDAI", 8, $.GRO, $.cDAI, $.COMP) public
	{
	}
}

/**
 * @notice Definition of gcUSDC. As a gcToken Type 1, it uses cUSDC as reserve
 * and employs leverage to maximize returns.
 */
contract gcUSDC is GCTokenType1
{
	constructor ()
		GCTokenType1("growth cUSDC v2", "gcUSDC", 8, $.GRO, $.cUSDC, $.COMP) public
	{
	}
}

/**
 * @notice Definition of gcETH. As a gcToken Type 2, it uses cETH as reserve
 * which serves as collateral for minting gDAI.
 */
contract gcETH is GCTokenType2
{
	constructor (address _growthToken)
		GCTokenType2("growth cETH", "gcETH", 8, $.GRO, $.cETH, $.COMP, $.cDAI, _growthToken) public
	{
	}

	receive() external payable {} // not to be used directly
}

/**
 * @notice Definition of gcWBTC. As a gcToken Type 2, it uses cWBTC as reserve
 * which serves as collateral for minting gDAI.
 */
contract gcWBTC is GCTokenType2
{
	constructor (address _growthToken)
		GCTokenType2("growth cWBTC", "gcWBTC", 8, $.GRO, $.cWBTC, $.COMP, $.cDAI, _growthToken) public
	{
	}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_sourceToken","type":"address"},{"indexed":true,"internalType":"address","name":"_targetToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_percent","type":"uint256"}],"name":"AnnounceTokenPercentTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_stakesAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_sharesAmount","type":"uint256"}],"name":"BurnLiquidityPoolPortion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_migrationRecipient","type":"address"}],"name":"CancelLiquidityPoolMigration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_oldPercent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newPercent","type":"uint256"}],"name":"ChangeTokenPercent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_migrationRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_stakesAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_sharesAmount","type":"uint256"}],"name":"CompleteLiquidityPoolMigration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_migrationRecipient","type":"address"}],"name":"InitiateLiquidityPoolMigration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"}],"name":"InsertToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"}],"name":"RemoveToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_sourceToken","type":"address"},{"indexed":true,"internalType":"address","name":"_targetToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_percent","type":"uint256"}],"name":"TransferTokenPercent","type":"event"},{"inputs":[{"internalType":"uint256","name":"_stakesAmount","type":"uint256"},{"internalType":"uint256","name":"_sharesAmount","type":"uint256"}],"name":"allocateLiquidityPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_sourceToken","type":"address"},{"internalType":"address","name":"_targetToken","type":"address"},{"internalType":"uint256","name":"_percent","type":"uint256"}],"name":"anounceTokenPercentTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnLiquidityPoolPortion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_netShares","type":"uint256"},{"internalType":"uint256","name":"_totalReserve","type":"uint256"},{"internalType":"uint256","name":"_totalSupply","type":"uint256"},{"internalType":"uint256","name":"_depositFee","type":"uint256"}],"name":"calcDepositCostFromShares","outputs":[{"internalType":"uint256","name":"_cost","type":"uint256"},{"internalType":"uint256","name":"_feeShares","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_cost","type":"uint256"},{"internalType":"uint256","name":"_totalReserve","type":"uint256"},{"internalType":"uint256","name":"_totalSupply","type":"uint256"},{"internalType":"uint256","name":"_depositFee","type":"uint256"}],"name":"calcDepositSharesFromCost","outputs":[{"internalType":"uint256","name":"_netShares","type":"uint256"},{"internalType":"uint256","name":"_feeShares","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_grossShares","type":"uint256"},{"internalType":"uint256","name":"_totalReserve","type":"uint256"},{"internalType":"uint256","name":"_totalSupply","type":"uint256"},{"internalType":"uint256","name":"_withdrawalFee","type":"uint256"}],"name":"calcWithdrawalCostFromShares","outputs":[{"internalType":"uint256","name":"_cost","type":"uint256"},{"internalType":"uint256","name":"_feeShares","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_cost","type":"uint256"},{"internalType":"uint256","name":"_totalReserve","type":"uint256"},{"internalType":"uint256","name":"_totalSupply","type":"uint256"},{"internalType":"uint256","name":"_withdrawalFee","type":"uint256"}],"name":"calcWithdrawalSharesFromCost","outputs":[{"internalType":"uint256","name":"_grossShares","type":"uint256"},{"internalType":"uint256","name":"_feeShares","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"cancelLiquidityPoolMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"completeLiquidityPoolMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_cost","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositFee","outputs":[{"internalType":"uint256","name":"_depositFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRebalanceMargins","outputs":[{"internalType":"uint256","name":"_liquidRebalanceMargin","type":"uint256"},{"internalType":"uint256","name":"_portfolioRebalanceMargin","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_migrationRecipient","type":"address"}],"name":"initiateLiquidityPoolMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"insertToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidityPool","outputs":[{"internalType":"address","name":"_liquidityPool","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityPoolBurningRate","outputs":[{"internalType":"uint256","name":"_burningRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityPoolLastBurningTime","outputs":[{"internalType":"uint256","name":"_lastBurningTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityPoolMigrationRecipient","outputs":[{"internalType":"address","name":"_migrationRecipient","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityPoolMigrationUnlockTime","outputs":[{"internalType":"uint256","name":"_migrationUnlockTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"removeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_burningRate","type":"uint256"}],"name":"setLiquidityPoolBurningRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_liquidRebalanceMargin","type":"uint256"},{"internalType":"uint256","name":"_portfolioRebalanceMargin","type":"uint256"}],"name":"setRebalanceMargins","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakesToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"tokenAt","outputs":[{"internalType":"address","name":"_token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenCount","outputs":[{"internalType":"uint256","name":"_count","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"tokenPercent","outputs":[{"internalType":"uint256","name":"_percent","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalReserve","outputs":[{"internalType":"uint256","name":"_totalReserve","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sourceToken","type":"address"},{"internalType":"address","name":"_targetToken","type":"address"},{"internalType":"uint256","name":"_percent","type":"uint256"}],"name":"transferTokenPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_grossShares","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalFee","outputs":[{"internalType":"uint256","name":"_withdrawalFee","type":"uint256"}],"stateMutability":"view","type":"function"}]

60c06040523480156200001157600080fd5b506040518060400160405280600b81526020017f67726f77746820574254430000000000000000000000000000000000000000008152506040518060400160405280600581526020017f675742544300000000000000000000000000000000000000000000000000000081525060087309e64c2b61a5f1690ee6fbed9baf5d6990f8dfd0732260fac5e5542a773aa44fbcfedf7c193bc2c599848484848484848160039080519060200190620000c992919062000351565b508060049080519060200190620000e292919062000351565b506012600560006101000a81548160ff021916908360ff16021790555050506000620001136200032b60201b60201c565b905080600560016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3506001600681905550620001cb836200033360201b60201c565b8173ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1660601b815250508073ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff1660601b81525050600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c63785cdc58909184306040518463ffffffff1660e01b8152600401620002799392919062000416565b60006040518083038186803b1580156200029257600080fd5b505af4158015620002a7573d6000803e3d6000fd5b505050505050505050600e73702281b181ffa845c48fd4729bdd7deca2e8858963d5c09fab9091836040518363ffffffff1660e01b8152600401620002ee92919062000453565b60006040518083038186803b1580156200030757600080fd5b505af41580156200031c573d6000803e3d6000fd5b505050505050505050620004b4565b600033905090565b80600560006101000a81548160ff021916908360ff16021790555050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200039457805160ff1916838001178555620003c5565b82800160010185558215620003c5579182015b82811115620003c4578251825591602001919060010190620003a7565b5b509050620003d49190620003d8565b5090565b5b80821115620003f3576000816000905550600101620003d9565b5090565b620004028162000480565b82525050565b8082525050565b8082525050565b60006060820190506200042d600083018662000408565b6200043c6020830185620003f7565b6200044b6040830184620003f7565b949350505050565b60006040820190506200046a60008301856200040f565b620004796020830184620003f7565b9392505050565b60006200048d8262000494565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60805160601c60a05160601c614eca620004fc60003980610e8e5280610fa45280612a9e5280612f695250806119cf52806124df52806128aa5280613c015250614eca6000f3fe608060405234801561001057600080fd5b506004361061027f5760003560e01c80637a1312521161015c578063a6823050116100ce578063e7d5f87911610087578063e7d5f8791461078e578063e8bd5bbd146107bf578063f2fde38b146107de578063f4325d67146107fa578063f496684314610818578063f98d98f7146108225761027f565b8063a6823050146106ce578063a9059cbb146106ec578063ab9c7db51461071c578063b6b55f2514610726578063cd08ae7b14610742578063dd62ed3e1461075e5761027f565b80639454e4c9116101205780639454e4c9146105f757806395d89b411461061357806397f04e251461063157806399ae0858146106625780639f181b5e14610680578063a457c2d71461069e5761027f565b80637a13125214610551578063865c6bc11461056f5780638bc7e8c41461058b5780638da5cb5b146105a957806392a91a3a146105c75761027f565b8063397808f6116101f5578063665a11ca116101b9578063665a11ca146104a357806367a52793146104c15780636b8e23ef146104df5780636f6e2156146104fb57806370a0823114610517578063715018a6146105475761027f565b8063397808f6146103fe5780634c68df671461042f5780635514fc8b1461044d5780635fa06b35146104575780635fa7b584146104875761027f565b806318160ddd1161024757806318160ddd1461032857806323b872dd146103465780632d870006146103765780632e1a7d4d14610394578063313ce567146103b057806339509351146103ce5761027f565b8063012abcd71461028457806306fdde03146102a2578063095ea7b3146102c05780630c8ad6f6146102f05780630d661d271461030c575b600080fd5b61028c610853565b6040516102999190614c35565b60405180910390f35b6102aa610860565b6040516102b7919061489d565b60405180910390f35b6102da60048036038101906102d591906140b4565b610902565b6040516102e79190614882565b60405180910390f35b61030a60048036038101906103059190613fd7565b610920565b005b610326600480360381019061032191906141ba565b610abf565b005b610330610c1e565b60405161033d9190614c35565b60405180910390f35b610360600480360381019061035b9190614065565b610c28565b60405161036d9190614882565b60405180910390f35b61037e610d01565b60405161038b9190614c35565b60405180910390f35b6103ae60048036038101906103a99190614168565b610d0e565b005b6103b8611048565b6040516103c59190614ca2565b60405180910390f35b6103e860048036038101906103e391906140b4565b61105f565b6040516103f59190614882565b60405180910390f35b61041860048036038101906104139190614232565b611112565b604051610426929190614c79565b60405180910390f35b61043761112e565b6040516104449190614c35565b60405180910390f35b6104556111bf565b005b610471600480360381019061046c9190613fd7565b6113f6565b60405161047e9190614c35565b60405180910390f35b6104a1600480360381019061049c9190613fd7565b61148b565b005b6104ab61162a565b6040516104b891906147f9565b60405180910390f35b6104c9611657565b6040516104d69190614c35565b60405180910390f35b6104f960048036038101906104f49190614065565b611702565b005b610515600480360381019061051091906141ba565b6118c9565b005b610531600480360381019061052c9190613fd7565b611ac4565b60405161053e9190614c35565b60405180910390f35b61054f611b0c565b005b610559611c64565b6040516105669190614c35565b60405180910390f35b61058960048036038101906105849190613fd7565b611c71565b005b610593611e10565b6040516105a09190614c35565b60405180910390f35b6105b1611eb5565b6040516105be91906147f9565b60405180910390f35b6105e160048036038101906105dc9190614168565b611edf565b6040516105ee91906147f9565b60405180910390f35b610611600480360381019061060c9190614065565b611f74565b005b61061b61241f565b604051610628919061489d565b60405180910390f35b61064b60048036038101906106469190614232565b6124c1565b604051610659929190614c79565b60405180910390f35b61066a6124dd565b60405161067791906147f9565b60405180910390f35b610688612501565b6040516106959190614c35565b60405180910390f35b6106b860048036038101906106b391906140b4565b612592565b6040516106c59190614882565b60405180910390f35b6106d661265f565b6040516106e391906147f9565b60405180910390f35b610706600480360381019061070191906140b4565b61268c565b6040516107139190614882565b60405180910390f35b6107246126aa565b005b610740600480360381019061073b9190614168565b612981565b005b61075c60048036038101906107579190614168565b612b8a565b005b61077860048036038101906107739190614029565b612ce6565b6040516107859190614c35565b60405180910390f35b6107a860048036038101906107a39190614232565b612d6d565b6040516107b6929190614c79565b60405180910390f35b6107c7612d89565b6040516107d5929190614c79565b60405180910390f35b6107f860048036038101906107f39190613fd7565b612da0565b005b610802612f67565b60405161080f91906147f9565b60405180910390f35b610820612f8b565b005b61083c60048036038101906108379190614232565b61314c565b60405161084a929190614c79565b60405180910390f35b6000600760040154905090565b606060038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156108f85780601f106108cd576101008083540402835291602001916108f8565b820191906000526020600020905b8154815290600101906020018083116108db57829003601f168201915b5050505050905090565b600061091661090f613168565b8484613170565b6001905092915050565b610928613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146109b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109ae9061499f565b60405180910390fd5b600260065414156109fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109f490614a3f565b60405180910390fd5b6002600681905550600e73702281b181ffa845c48fd4729bdd7deca2e88589633707e02b9091836040518363ffffffff1660e01b8152600401610a41929190614b3e565b60006040518083038186803b158015610a5957600080fd5b505af4158015610a6d573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff167fa79dcbc259bf75aee565a14f87df76336bb975bfda1d604945e7335dcba7490b60405160405180910390a2600160068190555050565b610ac7613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610b56576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4d9061499f565b60405180910390fd5b60026006541415610b9c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b9390614a3f565b60405180910390fd5b6002600681905550600e73702281b181ffa845c48fd4729bdd7deca2e8858963bff947ff909184846040518463ffffffff1660e01b8152600401610be293929190614bfe565b60006040518083038186803b158015610bfa57600080fd5b505af4158015610c0e573d6000803e3d6000fd5b5050505060016006819055505050565b6000600254905090565b6000610c3584848461333b565b610cf684610c41613168565b610cf185604051806060016040528060288152602001614e4860289139600160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610ca7613168565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546135d09092919063ffffffff16565b613170565b600190509392505050565b6000600760030154905090565b60026006541415610d54576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d4b90614a3f565b60405180910390fd5b6002600681905550600033905060008211610da4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d9b9061491f565b60405180910390fd5b600080610dc884610db361112e565b610dbb610c1e565b610dc3611e10565b61362b565b9150915060008211610e0f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e069061495f565b60405180910390fd5b610e18826136cd565b610e57576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e4e90614a1f565b60405180910390fd5b732f8bc65e6587b4ac6408c92a66209a9f1ef2ce08637ae2b5c783732f8bc65e6587b4ac6408c92a66209a9f1ef2ce0863f8b2cb4f7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b8152600401610ec991906147de565b60206040518083038186803b158015610ee157600080fd5b505af4158015610ef5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f199190614191565b6040518363ffffffff1660e01b8152600401610f36929190614c50565b60206040518083038186803b158015610f4e57600080fd5b505af4158015610f62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f869190614191565b9150732f8bc65e6587b4ac6408c92a66209a9f1ef2ce086312111b487f000000000000000000000000000000000000000000000000000000000000000085856040518463ffffffff1660e01b8152600401610fe39392919061484b565b60006040518083038186803b158015610ffb57600080fd5b505af415801561100f573d6000803e3d6000fd5b5050505061101d8385613762565b61103a3061103560028461391090919063ffffffff16565b61395a565b505050600160068190555050565b6000600560009054906101000a900460ff16905090565b600061110861106c613168565b84611103856001600061107d613168565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054613aee90919063ffffffff16565b613170565b6001905092915050565b60008061112186868686613b43565b9150915094509492505050565b6000600e73702281b181ffa845c48fd4729bdd7deca2e8858963e8b88e3790916040518263ffffffff1660e01b815260040161116a9190614b23565b60206040518083038186803b15801561118257600080fd5b505af4158015611196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ba9190614191565b905090565b6111c7613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611256576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161124d9061499f565b60405180910390fd5b6002600654141561129c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161129390614a3f565b60405180910390fd5b6002600681905550600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c631a70d5d590916040518263ffffffff1660e01b81526004016112de9190614a7f565b60006040518083038186803b1580156112f657600080fd5b505af415801561130a573d6000803e3d6000fd5b50505050600080600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c631b980af290916040518263ffffffff1660e01b815260040161134b9190614a7f565b604080518083038186803b15801561136257600080fd5b505af4158015611376573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139a91906141f6565b915091506113a782613be5565b6113b13082613762565b7f5de3db43e9093f1849590792cc2d3e9cda3d767d7180190ca7937c59f234b7d782826040516113e2929190614c79565b60405180910390a150506001600681905550565b6000600e73702281b181ffa845c48fd4729bdd7deca2e8858963aa66b9599091846040518363ffffffff1660e01b8152600401611434929190614b3e565b60206040518083038186803b15801561144c57600080fd5b505af4158015611460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114849190614191565b9050919050565b611493613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611522576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115199061499f565b60405180910390fd5b60026006541415611568576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161155f90614a3f565b60405180910390fd5b6002600681905550600e73702281b181ffa845c48fd4729bdd7deca2e88589635485109a9091836040518363ffffffff1660e01b81526004016115ac929190614b3e565b60006040518083038186803b1580156115c457600080fd5b505af41580156115d8573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff167f4eb129c82dcd3eedb52df2b0e6fb4cfa41ac64ee9d63ff081acbb1877e85d79b60405160405180910390a2600160068190555050565b6000600760020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c634571ca8a90916040518263ffffffff1660e01b81526004016116939190614a7f565b60206040518083038186803b1580156116ab57600080fd5b505af41580156116bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e3919061413f565b6116f457662386f26fc100006116fd565b66470de4df8200005b905090565b61170a613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611799576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117909061499f565b60405180910390fd5b600260065414156117df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117d690614a3f565b60405180910390fd5b6002600681905550600e73702281b181ffa845c48fd4729bdd7deca2e88589636a3b3c4890918585856040518563ffffffff1660e01b81526004016118279493929190614b67565b60006040518083038186803b15801561183f57600080fd5b505af4158015611853573d6000803e3d6000fd5b505050508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb1c6ca6ef7ac125ef8fac95b43a0cbc9f154320d0382cc38b5ae880560ff05a6836040516118b49190614c35565b60405180910390a36001600681905550505050565b6118d1613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119579061499f565b60405180910390fd5b600260065414156119a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161199d90614a3f565b60405180910390fd5b60026006819055506000339050732f8bc65e6587b4ac6408c92a66209a9f1ef2ce0863201add9b7f000000000000000000000000000000000000000000000000000000000000000083866040518463ffffffff1660e01b8152600401611a0e9392919061484b565b60006040518083038186803b158015611a2657600080fd5b505af4158015611a3a573d6000803e3d6000fd5b50505050611a4981308461333b565b600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c63b2f9dd74909185856040518463ffffffff1660e01b8152600401611a8793929190614aec565b60006040518083038186803b158015611a9f57600080fd5b505af4158015611ab3573d6000803e3d6000fd5b505050505060016006819055505050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b611b14613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611ba3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b9a9061499f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a36000600560016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000600760060154905090565b611c79613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611d08576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cff9061499f565b60405180910390fd5b60026006541415611d4e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d4590614a3f565b60405180910390fd5b6002600681905550600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c635de508599091836040518363ffffffff1660e01b8152600401611d92929190614a9a565b60006040518083038186803b158015611daa57600080fd5b505af4158015611dbe573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff167f2da3691fd2703bfd89093c0616be6ea99148d085f6236cda85ffc3af9071b00660405160405180910390a2600160068190555050565b6000600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c634571ca8a90916040518263ffffffff1660e01b8152600401611e4c9190614a7f565b60206040518083038186803b158015611e6457600080fd5b505af4158015611e78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e9c919061413f565b611ead57662386f26fc10000611eb0565b60005b905090565b6000600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000600e73702281b181ffa845c48fd4729bdd7deca2e88589636441d94a9091846040518363ffffffff1660e01b8152600401611f1d929190614bd5565b60206040518083038186803b158015611f3557600080fd5b505af4158015611f49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f6d9190614000565b9050919050565b611f7c613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461200b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120029061499f565b60405180910390fd5b60026006541415612051576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161204890614a3f565b60405180910390fd5b60026006819055506000600e73702281b181ffa845c48fd4729bdd7deca2e8858963aa66b9599091866040518363ffffffff1660e01b8152600401612097929190614b3e565b60206040518083038186803b1580156120af57600080fd5b505af41580156120c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120e79190614191565b90506000600e73702281b181ffa845c48fd4729bdd7deca2e8858963aa66b9599091866040518363ffffffff1660e01b8152600401612127929190614b3e565b60206040518083038186803b15801561213f57600080fd5b505af4158015612153573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121779190614191565b9050600e73702281b181ffa845c48fd4729bdd7deca2e885896348fefaf590918787876040518563ffffffff1660e01b81526004016121b99493929190614b67565b60006040518083038186803b1580156121d157600080fd5b505af41580156121e5573d6000803e3d6000fd5b505050506000600e73702281b181ffa845c48fd4729bdd7deca2e8858963aa66b9599091886040518363ffffffff1660e01b8152600401612227929190614b3e565b60206040518083038186803b15801561223f57600080fd5b505af4158015612253573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122779190614191565b90506000600e73702281b181ffa845c48fd4729bdd7deca2e8858963aa66b9599091886040518363ffffffff1660e01b81526004016122b7929190614b3e565b60206040518083038186803b1580156122cf57600080fd5b505af41580156122e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123079190614191565b90508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167f144f9c43e92cb240687216176e18d61c36cef9fc814aab1ab14bc55053bf28be876040516123669190614c35565b60405180910390a38673ffffffffffffffffffffffffffffffffffffffff167f8e15c6e5092a774f2fcec7208c9007cf9f744ca4363e3e92cff261b3403298a985846040516123b6929190614c79565b60405180910390a28573ffffffffffffffffffffffffffffffffffffffff167f8e15c6e5092a774f2fcec7208c9007cf9f744ca4363e3e92cff261b3403298a98483604051612406929190614c79565b60405180910390a2505050506001600681905550505050565b606060048054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156124b75780601f1061248c576101008083540402835291602001916124b7565b820191906000526020600020905b81548152906001019060200180831161249a57829003601f168201915b5050505050905090565b6000806124d08686868661362b565b9150915094509492505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000600e73702281b181ffa845c48fd4729bdd7deca2e885896394d70c3490916040518263ffffffff1660e01b815260040161253d9190614b23565b60206040518083038186803b15801561255557600080fd5b505af4158015612569573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061258d9190614191565b905090565b600061265561259f613168565b8461265085604051806060016040528060258152602001614e7060259139600160006125c9613168565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546135d09092919063ffffffff16565b613170565b6001905092915050565b6000600760050160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006126a0612699613168565b848461333b565b6001905092915050565b6126b2613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612741576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127389061499f565b60405180910390fd5b60026006541415612787576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161277e90614a3f565b60405180910390fd5b6002600681905550600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c631a70d5d590916040518263ffffffff1660e01b81526004016127c99190614a7f565b60006040518083038186803b1580156127e157600080fd5b505af41580156127f5573d6000803e3d6000fd5b505050506000806000600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c63fc3b6efd90916040518263ffffffff1660e01b81526004016128389190614a7f565b60606040518083038186803b15801561285057600080fd5b505af4158015612864573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061288891906140f0565b925092509250732f8bc65e6587b4ac6408c92a66209a9f1ef2ce086312111b487f000000000000000000000000000000000000000000000000000000000000000085856040518463ffffffff1660e01b81526004016128e99392919061484b565b60006040518083038186803b15801561290157600080fd5b505af4158015612915573d6000803e3d6000fd5b5050505061292430848361333b565b8273ffffffffffffffffffffffffffffffffffffffff167f32ff16b7e2d85978fd6c5c010a46b09c8cd011cc35fd2c69c214f9f188756b60838360405161296c929190614c79565b60405180910390a25050506001600681905550565b600260065414156129c7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129be90614a3f565b60405180910390fd5b6002600681905550600033905060008211612a17576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a0e9061495f565b60405180910390fd5b600080612a3b84612a2661112e565b612a2e610c1e565b612a36611657565b613c74565b9150915060008211612a82576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a799061491f565b60405180910390fd5b732f8bc65e6587b4ac6408c92a66209a9f1ef2ce0863201add9b7f000000000000000000000000000000000000000000000000000000000000000085876040518463ffffffff1660e01b8152600401612add9392919061484b565b60006040518083038186803b158015612af557600080fd5b505af4158015612b09573d6000803e3d6000fd5b50505050612b1684613d16565b612b55576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b4c90614a1f565b60405180910390fd5b612b5f838361395a565b612b7c30612b7760028461391090919063ffffffff16565b61395a565b505050600160068190555050565b612b92613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612c21576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c189061499f565b60405180910390fd5b60026006541415612c67576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c5e90614a3f565b60405180910390fd5b6002600681905550600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c632fc518b29091836040518363ffffffff1660e01b8152600401612cab929190614ac3565b60006040518083038186803b158015612cc357600080fd5b505af4158015612cd7573d6000803e3d6000fd5b50505050600160068190555050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600080612d7c86868686613dac565b9150915094509492505050565b600080600e60050154600e60060154915091509091565b612da8613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612e37576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e2e9061499f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612ea7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e9e906148df565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600560016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b612f93613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613022576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130199061499f565b60405180910390fd5b60026006541415613068576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161305f90614a3f565b60405180910390fd5b60026006819055506000600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c630eaced8190916040518263ffffffff1660e01b81526004016130ac9190614a7f565b60206040518083038186803b1580156130c457600080fd5b505af41580156130d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130fc9190614000565b90508073ffffffffffffffffffffffffffffffffffffffff167f2d38b612ec22e51eb40a71fe7d58e1a7ebb9989673619ae9f96cc0da907773d360405160405180910390a2506001600681905550565b60008061315b86868686613c74565b9150915094509492505050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156131e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016131d7906149ff565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415613250576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613247906148ff565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258360405161332e9190614c35565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156133ab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016133a2906149df565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561341b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613412906148bf565b60405180910390fd5b613426838383613e4e565b61349181604051806060016040528060268152602001614e22602691396000808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546135d09092919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550613524816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054613aee90919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516135c39190614c35565b60405180910390a3505050565b6000838311158290613618576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161360f919061489d565b60405180910390fd5b5060008385039050809150509392505050565b6000806000613677670de0b6b3a764000061366961365a87670de0b6b3a7640000613e5390919063ffffffff16565b8a613e9d90919063ffffffff16565b61391090919063ffffffff16565b90508481146136aa576136a5856136978884613e9d90919063ffffffff16565b61391090919063ffffffff16565b6136ac565b855b92506136c18188613e5390919063ffffffff16565b91505094509492505050565b6000600e73702281b181ffa845c48fd4729bdd7deca2e88589637f1dc4549091846040518363ffffffff1660e01b815260040161370b929190614bd5565b60206040518083038186803b15801561372357600080fd5b505af4158015613737573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061375b919061413f565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156137d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016137c9906149bf565b60405180910390fd5b6137de82600083613e4e565b61384981604051806060016040528060228152602001614e00602291396000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546135d09092919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506138a081600254613e5390919063ffffffff16565b600281905550600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516139049190614c35565b60405180910390a35050565b600061395283836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613f0d565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156139ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016139c190614a5f565b60405180910390fd5b6139d660008383613e4e565b6139eb81600254613aee90919063ffffffff16565b600281905550613a42816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054613aee90919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051613ae29190614c35565b60405180910390a35050565b600080828401905083811015613b39576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613b309061493f565b60405180910390fd5b8091505092915050565b6000806000858714613b7957613b7486613b66878a613e9d90919063ffffffff16565b61391090919063ffffffff16565b613b7b565b845b9050613bc4613b9b85670de0b6b3a7640000613e5390919063ffffffff16565b613bb6670de0b6b3a764000084613e9d90919063ffffffff16565b61391090919063ffffffff16565b9250613bd98184613e5390919063ffffffff16565b91505094509492505050565b732f8bc65e6587b4ac6408c92a66209a9f1ef2ce086312111b487f00000000000000000000000000000000000000000000000000000000000000006000846040518463ffffffff1660e01b8152600401613c4193929190614814565b60006040518083038186803b158015613c5957600080fd5b505af4158015613c6d573d6000803e3d6000fd5b5050505050565b6000806000858514613caa57613ca586613c97878a613e9d90919063ffffffff16565b61391090919063ffffffff16565b613cac565b865b9050613cf5670de0b6b3a7640000613ce7613cd887670de0b6b3a7640000613e5390919063ffffffff16565b84613e9d90919063ffffffff16565b61391090919063ffffffff16565b9250613d0a8382613e5390919063ffffffff16565b91505094509492505050565b6000600e73702281b181ffa845c48fd4729bdd7deca2e88589637f1dc454909160006040518363ffffffff1660e01b8152600401613d55929190614bac565b60206040518083038186803b158015613d6d57600080fd5b505af4158015613d81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613da5919061413f565b9050919050565b6000806000613df8613dcf85670de0b6b3a7640000613e5390919063ffffffff16565b613dea670de0b6b3a76400008a613e9d90919063ffffffff16565b61391090919063ffffffff16565b9050848614613e2b57613e2685613e188884613e9d90919063ffffffff16565b61391090919063ffffffff16565b613e2d565b805b9250613e428782613e5390919063ffffffff16565b91505094509492505050565b505050565b6000613e9583836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506135d0565b905092915050565b600080831415613eb05760009050613f07565b6000828402905082848281613ec157fe5b0414613f02576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613ef99061497f565b60405180910390fd5b809150505b92915050565b60008083118290613f54576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613f4b919061489d565b60405180910390fd5b506000838581613f6057fe5b049050809150509392505050565b600081359050613f7d81614dba565b92915050565b600081519050613f9281614dba565b92915050565b600081519050613fa781614dd1565b92915050565b600081359050613fbc81614de8565b92915050565b600081519050613fd181614de8565b92915050565b600060208284031215613fe957600080fd5b6000613ff784828501613f6e565b91505092915050565b60006020828403121561401257600080fd5b600061402084828501613f83565b91505092915050565b6000806040838503121561403c57600080fd5b600061404a85828601613f6e565b925050602061405b85828601613f6e565b9150509250929050565b60008060006060848603121561407a57600080fd5b600061408886828701613f6e565b935050602061409986828701613f6e565b92505060406140aa86828701613fad565b9150509250925092565b600080604083850312156140c757600080fd5b60006140d585828601613f6e565b92505060206140e685828601613fad565b9150509250929050565b60008060006060848603121561410557600080fd5b600061411386828701613f83565b935050602061412486828701613fc2565b925050604061413586828701613fc2565b9150509250925092565b60006020828403121561415157600080fd5b600061415f84828501613f98565b91505092915050565b60006020828403121561417a57600080fd5b600061418884828501613fad565b91505092915050565b6000602082840312156141a357600080fd5b60006141b184828501613fc2565b91505092915050565b600080604083850312156141cd57600080fd5b60006141db85828601613fad565b92505060206141ec85828601613fad565b9150509250929050565b6000806040838503121561420957600080fd5b600061421785828601613fc2565b925050602061422885828601613fc2565b9150509250929050565b6000806000806080858703121561424857600080fd5b600061425687828801613fad565b945050602061426787828801613fad565b935050604061427887828801613fad565b925050606061428987828801613fad565b91505092959194509250565b61429e81614d2e565b82525050565b6142ad81614cd9565b82525050565b6142bc81614cd9565b82525050565b6142cb81614ceb565b82525050565b6142da81614d40565b82525050565b60006142eb82614cbd565b6142f58185614cc8565b9350614305818560208601614d76565b61430e81614da9565b840191505092915050565b6000614326602383614cc8565b91507f45524332303a207472616e7366657220746f20746865207a65726f206164647260008301527f65737300000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b600061438c602683614cc8565b91507f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008301527f64647265737300000000000000000000000000000000000000000000000000006020830152604082019050919050565b60006143f2602283614cc8565b91507f45524332303a20617070726f766520746f20746865207a65726f20616464726560008301527f73730000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000614458601d83614cc8565b91507f736861726573206d7573742062652067726561746572207468616e20300000006000830152602082019050919050565b6000614498601b83614cc8565b91507f536166654d6174683a206164646974696f6e206f766572666c6f7700000000006000830152602082019050919050565b60006144d8601b83614cc8565b91507f636f7374206d7573742062652067726561746572207468616e203000000000006000830152602082019050919050565b6000614518602183614cc8565b91507f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f60008301527f77000000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b600061457e602083614cc8565b91507f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726000830152602082019050919050565b60006145be602183614cc8565b91507f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008301527f73000000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000614624602583614cc8565b91507f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008301527f64726573730000000000000000000000000000000000000000000000000000006020830152604082019050919050565b600061468a602483614cc8565b91507f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008301527f72657373000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b60006146f0601b83614cc8565b91507f6e6f7420617661696c61626c6520617420746865206d6f6d656e7400000000006000830152602082019050919050565b6000614730601f83614cc8565b91507f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006000830152602082019050919050565b6000614770601f83614cc8565b91507f45524332303a206d696e7420746f20746865207a65726f2061646472657373006000830152602082019050919050565b8082525050565b8082525050565b6147ba81614d17565b82525050565b6147c981614d17565b82525050565b6147d881614d21565b82525050565b60006020820190506147f360008301846142b3565b92915050565b600060208201905061480e60008301846142a4565b92915050565b600060608201905061482960008301866142b3565b6148366020830185614295565b61484360408301846147c0565b949350505050565b600060608201905061486060008301866142b3565b61486d60208301856142b3565b61487a60408301846147c0565b949350505050565b600060208201905061489760008301846142c2565b92915050565b600060208201905081810360008301526148b781846142e0565b905092915050565b600060208201905081810360008301526148d881614319565b9050919050565b600060208201905081810360008301526148f88161437f565b9050919050565b60006020820190508181036000830152614918816143e5565b9050919050565b600060208201905081810360008301526149388161444b565b9050919050565b600060208201905081810360008301526149588161448b565b9050919050565b60006020820190508181036000830152614978816144cb565b9050919050565b600060208201905081810360008301526149988161450b565b9050919050565b600060208201905081810360008301526149b881614571565b9050919050565b600060208201905081810360008301526149d8816145b1565b9050919050565b600060208201905081810360008301526149f881614617565b9050919050565b60006020820190508181036000830152614a188161467d565b9050919050565b60006020820190508181036000830152614a38816146e3565b9050919050565b60006020820190508181036000830152614a5881614723565b9050919050565b60006020820190508181036000830152614a7881614763565b9050919050565b6000602082019050614a9460008301846147a3565b92915050565b6000604082019050614aaf60008301856147a3565b614abc60208301846142b3565b9392505050565b6000604082019050614ad860008301856147a3565b614ae560208301846147c0565b9392505050565b6000606082019050614b0160008301866147a3565b614b0e60208301856147c0565b614b1b60408301846147c0565b949350505050565b6000602082019050614b3860008301846147aa565b92915050565b6000604082019050614b5360008301856147aa565b614b6060208301846142b3565b9392505050565b6000608082019050614b7c60008301876147aa565b614b8960208301866142b3565b614b9660408301856142b3565b614ba360608301846147c0565b95945050505050565b6000604082019050614bc160008301856147aa565b614bce60208301846142d1565b9392505050565b6000604082019050614bea60008301856147aa565b614bf760208301846147c0565b9392505050565b6000606082019050614c1360008301866147aa565b614c2060208301856147c0565b614c2d60408301846147c0565b949350505050565b6000602082019050614c4a60008301846147b1565b92915050565b6000604082019050614c6560008301856147c0565b614c7260208301846147c0565b9392505050565b6000604082019050614c8e60008301856147b1565b614c9b60208301846147b1565b9392505050565b6000602082019050614cb760008301846147cf565b92915050565b600081519050919050565b600082825260208201905092915050565b6000614ce482614cf7565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b6000614d3982614d52565b9050919050565b6000614d4b82614d17565b9050919050565b6000614d5d82614d64565b9050919050565b6000614d6f82614cf7565b9050919050565b60005b83811015614d94578082015181840152602081019050614d79565b83811115614da3576000848401525b50505050565b6000601f19601f8301169050919050565b614dc381614cd9565b8114614dce57600080fd5b50565b614dda81614ceb565b8114614de557600080fd5b50565b614df181614d17565b8114614dfc57600080fd5b5056fe45524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220af02cf2834dd6c405bec5d33131a75a446b53c5e95d953bc3cef39f94eaf1f8f64736f6c634300060c0033

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061027f5760003560e01c80637a1312521161015c578063a6823050116100ce578063e7d5f87911610087578063e7d5f8791461078e578063e8bd5bbd146107bf578063f2fde38b146107de578063f4325d67146107fa578063f496684314610818578063f98d98f7146108225761027f565b8063a6823050146106ce578063a9059cbb146106ec578063ab9c7db51461071c578063b6b55f2514610726578063cd08ae7b14610742578063dd62ed3e1461075e5761027f565b80639454e4c9116101205780639454e4c9146105f757806395d89b411461061357806397f04e251461063157806399ae0858146106625780639f181b5e14610680578063a457c2d71461069e5761027f565b80637a13125214610551578063865c6bc11461056f5780638bc7e8c41461058b5780638da5cb5b146105a957806392a91a3a146105c75761027f565b8063397808f6116101f5578063665a11ca116101b9578063665a11ca146104a357806367a52793146104c15780636b8e23ef146104df5780636f6e2156146104fb57806370a0823114610517578063715018a6146105475761027f565b8063397808f6146103fe5780634c68df671461042f5780635514fc8b1461044d5780635fa06b35146104575780635fa7b584146104875761027f565b806318160ddd1161024757806318160ddd1461032857806323b872dd146103465780632d870006146103765780632e1a7d4d14610394578063313ce567146103b057806339509351146103ce5761027f565b8063012abcd71461028457806306fdde03146102a2578063095ea7b3146102c05780630c8ad6f6146102f05780630d661d271461030c575b600080fd5b61028c610853565b6040516102999190614c35565b60405180910390f35b6102aa610860565b6040516102b7919061489d565b60405180910390f35b6102da60048036038101906102d591906140b4565b610902565b6040516102e79190614882565b60405180910390f35b61030a60048036038101906103059190613fd7565b610920565b005b610326600480360381019061032191906141ba565b610abf565b005b610330610c1e565b60405161033d9190614c35565b60405180910390f35b610360600480360381019061035b9190614065565b610c28565b60405161036d9190614882565b60405180910390f35b61037e610d01565b60405161038b9190614c35565b60405180910390f35b6103ae60048036038101906103a99190614168565b610d0e565b005b6103b8611048565b6040516103c59190614ca2565b60405180910390f35b6103e860048036038101906103e391906140b4565b61105f565b6040516103f59190614882565b60405180910390f35b61041860048036038101906104139190614232565b611112565b604051610426929190614c79565b60405180910390f35b61043761112e565b6040516104449190614c35565b60405180910390f35b6104556111bf565b005b610471600480360381019061046c9190613fd7565b6113f6565b60405161047e9190614c35565b60405180910390f35b6104a1600480360381019061049c9190613fd7565b61148b565b005b6104ab61162a565b6040516104b891906147f9565b60405180910390f35b6104c9611657565b6040516104d69190614c35565b60405180910390f35b6104f960048036038101906104f49190614065565b611702565b005b610515600480360381019061051091906141ba565b6118c9565b005b610531600480360381019061052c9190613fd7565b611ac4565b60405161053e9190614c35565b60405180910390f35b61054f611b0c565b005b610559611c64565b6040516105669190614c35565b60405180910390f35b61058960048036038101906105849190613fd7565b611c71565b005b610593611e10565b6040516105a09190614c35565b60405180910390f35b6105b1611eb5565b6040516105be91906147f9565b60405180910390f35b6105e160048036038101906105dc9190614168565b611edf565b6040516105ee91906147f9565b60405180910390f35b610611600480360381019061060c9190614065565b611f74565b005b61061b61241f565b604051610628919061489d565b60405180910390f35b61064b60048036038101906106469190614232565b6124c1565b604051610659929190614c79565b60405180910390f35b61066a6124dd565b60405161067791906147f9565b60405180910390f35b610688612501565b6040516106959190614c35565b60405180910390f35b6106b860048036038101906106b391906140b4565b612592565b6040516106c59190614882565b60405180910390f35b6106d661265f565b6040516106e391906147f9565b60405180910390f35b610706600480360381019061070191906140b4565b61268c565b6040516107139190614882565b60405180910390f35b6107246126aa565b005b610740600480360381019061073b9190614168565b612981565b005b61075c60048036038101906107579190614168565b612b8a565b005b61077860048036038101906107739190614029565b612ce6565b6040516107859190614c35565b60405180910390f35b6107a860048036038101906107a39190614232565b612d6d565b6040516107b6929190614c79565b60405180910390f35b6107c7612d89565b6040516107d5929190614c79565b60405180910390f35b6107f860048036038101906107f39190613fd7565b612da0565b005b610802612f67565b60405161080f91906147f9565b60405180910390f35b610820612f8b565b005b61083c60048036038101906108379190614232565b61314c565b60405161084a929190614c79565b60405180910390f35b6000600760040154905090565b606060038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156108f85780601f106108cd576101008083540402835291602001916108f8565b820191906000526020600020905b8154815290600101906020018083116108db57829003601f168201915b5050505050905090565b600061091661090f613168565b8484613170565b6001905092915050565b610928613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146109b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109ae9061499f565b60405180910390fd5b600260065414156109fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109f490614a3f565b60405180910390fd5b6002600681905550600e73702281b181ffa845c48fd4729bdd7deca2e88589633707e02b9091836040518363ffffffff1660e01b8152600401610a41929190614b3e565b60006040518083038186803b158015610a5957600080fd5b505af4158015610a6d573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff167fa79dcbc259bf75aee565a14f87df76336bb975bfda1d604945e7335dcba7490b60405160405180910390a2600160068190555050565b610ac7613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610b56576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4d9061499f565b60405180910390fd5b60026006541415610b9c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b9390614a3f565b60405180910390fd5b6002600681905550600e73702281b181ffa845c48fd4729bdd7deca2e8858963bff947ff909184846040518463ffffffff1660e01b8152600401610be293929190614bfe565b60006040518083038186803b158015610bfa57600080fd5b505af4158015610c0e573d6000803e3d6000fd5b5050505060016006819055505050565b6000600254905090565b6000610c3584848461333b565b610cf684610c41613168565b610cf185604051806060016040528060288152602001614e4860289139600160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610ca7613168565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546135d09092919063ffffffff16565b613170565b600190509392505050565b6000600760030154905090565b60026006541415610d54576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d4b90614a3f565b60405180910390fd5b6002600681905550600033905060008211610da4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d9b9061491f565b60405180910390fd5b600080610dc884610db361112e565b610dbb610c1e565b610dc3611e10565b61362b565b9150915060008211610e0f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e069061495f565b60405180910390fd5b610e18826136cd565b610e57576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e4e90614a1f565b60405180910390fd5b732f8bc65e6587b4ac6408c92a66209a9f1ef2ce08637ae2b5c783732f8bc65e6587b4ac6408c92a66209a9f1ef2ce0863f8b2cb4f7f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5996040518263ffffffff1660e01b8152600401610ec991906147de565b60206040518083038186803b158015610ee157600080fd5b505af4158015610ef5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f199190614191565b6040518363ffffffff1660e01b8152600401610f36929190614c50565b60206040518083038186803b158015610f4e57600080fd5b505af4158015610f62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f869190614191565b9150732f8bc65e6587b4ac6408c92a66209a9f1ef2ce086312111b487f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59985856040518463ffffffff1660e01b8152600401610fe39392919061484b565b60006040518083038186803b158015610ffb57600080fd5b505af415801561100f573d6000803e3d6000fd5b5050505061101d8385613762565b61103a3061103560028461391090919063ffffffff16565b61395a565b505050600160068190555050565b6000600560009054906101000a900460ff16905090565b600061110861106c613168565b84611103856001600061107d613168565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054613aee90919063ffffffff16565b613170565b6001905092915050565b60008061112186868686613b43565b9150915094509492505050565b6000600e73702281b181ffa845c48fd4729bdd7deca2e8858963e8b88e3790916040518263ffffffff1660e01b815260040161116a9190614b23565b60206040518083038186803b15801561118257600080fd5b505af4158015611196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ba9190614191565b905090565b6111c7613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611256576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161124d9061499f565b60405180910390fd5b6002600654141561129c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161129390614a3f565b60405180910390fd5b6002600681905550600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c631a70d5d590916040518263ffffffff1660e01b81526004016112de9190614a7f565b60006040518083038186803b1580156112f657600080fd5b505af415801561130a573d6000803e3d6000fd5b50505050600080600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c631b980af290916040518263ffffffff1660e01b815260040161134b9190614a7f565b604080518083038186803b15801561136257600080fd5b505af4158015611376573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139a91906141f6565b915091506113a782613be5565b6113b13082613762565b7f5de3db43e9093f1849590792cc2d3e9cda3d767d7180190ca7937c59f234b7d782826040516113e2929190614c79565b60405180910390a150506001600681905550565b6000600e73702281b181ffa845c48fd4729bdd7deca2e8858963aa66b9599091846040518363ffffffff1660e01b8152600401611434929190614b3e565b60206040518083038186803b15801561144c57600080fd5b505af4158015611460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114849190614191565b9050919050565b611493613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611522576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115199061499f565b60405180910390fd5b60026006541415611568576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161155f90614a3f565b60405180910390fd5b6002600681905550600e73702281b181ffa845c48fd4729bdd7deca2e88589635485109a9091836040518363ffffffff1660e01b81526004016115ac929190614b3e565b60006040518083038186803b1580156115c457600080fd5b505af41580156115d8573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff167f4eb129c82dcd3eedb52df2b0e6fb4cfa41ac64ee9d63ff081acbb1877e85d79b60405160405180910390a2600160068190555050565b6000600760020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c634571ca8a90916040518263ffffffff1660e01b81526004016116939190614a7f565b60206040518083038186803b1580156116ab57600080fd5b505af41580156116bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e3919061413f565b6116f457662386f26fc100006116fd565b66470de4df8200005b905090565b61170a613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611799576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117909061499f565b60405180910390fd5b600260065414156117df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117d690614a3f565b60405180910390fd5b6002600681905550600e73702281b181ffa845c48fd4729bdd7deca2e88589636a3b3c4890918585856040518563ffffffff1660e01b81526004016118279493929190614b67565b60006040518083038186803b15801561183f57600080fd5b505af4158015611853573d6000803e3d6000fd5b505050508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb1c6ca6ef7ac125ef8fac95b43a0cbc9f154320d0382cc38b5ae880560ff05a6836040516118b49190614c35565b60405180910390a36001600681905550505050565b6118d1613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611960576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119579061499f565b60405180910390fd5b600260065414156119a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161199d90614a3f565b60405180910390fd5b60026006819055506000339050732f8bc65e6587b4ac6408c92a66209a9f1ef2ce0863201add9b7f00000000000000000000000009e64c2b61a5f1690ee6fbed9baf5d6990f8dfd083866040518463ffffffff1660e01b8152600401611a0e9392919061484b565b60006040518083038186803b158015611a2657600080fd5b505af4158015611a3a573d6000803e3d6000fd5b50505050611a4981308461333b565b600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c63b2f9dd74909185856040518463ffffffff1660e01b8152600401611a8793929190614aec565b60006040518083038186803b158015611a9f57600080fd5b505af4158015611ab3573d6000803e3d6000fd5b505050505060016006819055505050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b611b14613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611ba3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b9a9061499f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a36000600560016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000600760060154905090565b611c79613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611d08576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cff9061499f565b60405180910390fd5b60026006541415611d4e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d4590614a3f565b60405180910390fd5b6002600681905550600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c635de508599091836040518363ffffffff1660e01b8152600401611d92929190614a9a565b60006040518083038186803b158015611daa57600080fd5b505af4158015611dbe573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff167f2da3691fd2703bfd89093c0616be6ea99148d085f6236cda85ffc3af9071b00660405160405180910390a2600160068190555050565b6000600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c634571ca8a90916040518263ffffffff1660e01b8152600401611e4c9190614a7f565b60206040518083038186803b158015611e6457600080fd5b505af4158015611e78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e9c919061413f565b611ead57662386f26fc10000611eb0565b60005b905090565b6000600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000600e73702281b181ffa845c48fd4729bdd7deca2e88589636441d94a9091846040518363ffffffff1660e01b8152600401611f1d929190614bd5565b60206040518083038186803b158015611f3557600080fd5b505af4158015611f49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f6d9190614000565b9050919050565b611f7c613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461200b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120029061499f565b60405180910390fd5b60026006541415612051576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161204890614a3f565b60405180910390fd5b60026006819055506000600e73702281b181ffa845c48fd4729bdd7deca2e8858963aa66b9599091866040518363ffffffff1660e01b8152600401612097929190614b3e565b60206040518083038186803b1580156120af57600080fd5b505af41580156120c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120e79190614191565b90506000600e73702281b181ffa845c48fd4729bdd7deca2e8858963aa66b9599091866040518363ffffffff1660e01b8152600401612127929190614b3e565b60206040518083038186803b15801561213f57600080fd5b505af4158015612153573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121779190614191565b9050600e73702281b181ffa845c48fd4729bdd7deca2e885896348fefaf590918787876040518563ffffffff1660e01b81526004016121b99493929190614b67565b60006040518083038186803b1580156121d157600080fd5b505af41580156121e5573d6000803e3d6000fd5b505050506000600e73702281b181ffa845c48fd4729bdd7deca2e8858963aa66b9599091886040518363ffffffff1660e01b8152600401612227929190614b3e565b60206040518083038186803b15801561223f57600080fd5b505af4158015612253573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122779190614191565b90506000600e73702281b181ffa845c48fd4729bdd7deca2e8858963aa66b9599091886040518363ffffffff1660e01b81526004016122b7929190614b3e565b60206040518083038186803b1580156122cf57600080fd5b505af41580156122e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123079190614191565b90508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167f144f9c43e92cb240687216176e18d61c36cef9fc814aab1ab14bc55053bf28be876040516123669190614c35565b60405180910390a38673ffffffffffffffffffffffffffffffffffffffff167f8e15c6e5092a774f2fcec7208c9007cf9f744ca4363e3e92cff261b3403298a985846040516123b6929190614c79565b60405180910390a28573ffffffffffffffffffffffffffffffffffffffff167f8e15c6e5092a774f2fcec7208c9007cf9f744ca4363e3e92cff261b3403298a98483604051612406929190614c79565b60405180910390a2505050506001600681905550505050565b606060048054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156124b75780601f1061248c576101008083540402835291602001916124b7565b820191906000526020600020905b81548152906001019060200180831161249a57829003601f168201915b5050505050905090565b6000806124d08686868661362b565b9150915094509492505050565b7f00000000000000000000000009e64c2b61a5f1690ee6fbed9baf5d6990f8dfd081565b6000600e73702281b181ffa845c48fd4729bdd7deca2e885896394d70c3490916040518263ffffffff1660e01b815260040161253d9190614b23565b60206040518083038186803b15801561255557600080fd5b505af4158015612569573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061258d9190614191565b905090565b600061265561259f613168565b8461265085604051806060016040528060258152602001614e7060259139600160006125c9613168565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546135d09092919063ffffffff16565b613170565b6001905092915050565b6000600760050160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60006126a0612699613168565b848461333b565b6001905092915050565b6126b2613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612741576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127389061499f565b60405180910390fd5b60026006541415612787576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161277e90614a3f565b60405180910390fd5b6002600681905550600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c631a70d5d590916040518263ffffffff1660e01b81526004016127c99190614a7f565b60006040518083038186803b1580156127e157600080fd5b505af41580156127f5573d6000803e3d6000fd5b505050506000806000600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c63fc3b6efd90916040518263ffffffff1660e01b81526004016128389190614a7f565b60606040518083038186803b15801561285057600080fd5b505af4158015612864573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061288891906140f0565b925092509250732f8bc65e6587b4ac6408c92a66209a9f1ef2ce086312111b487f00000000000000000000000009e64c2b61a5f1690ee6fbed9baf5d6990f8dfd085856040518463ffffffff1660e01b81526004016128e99392919061484b565b60006040518083038186803b15801561290157600080fd5b505af4158015612915573d6000803e3d6000fd5b5050505061292430848361333b565b8273ffffffffffffffffffffffffffffffffffffffff167f32ff16b7e2d85978fd6c5c010a46b09c8cd011cc35fd2c69c214f9f188756b60838360405161296c929190614c79565b60405180910390a25050506001600681905550565b600260065414156129c7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129be90614a3f565b60405180910390fd5b6002600681905550600033905060008211612a17576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a0e9061495f565b60405180910390fd5b600080612a3b84612a2661112e565b612a2e610c1e565b612a36611657565b613c74565b9150915060008211612a82576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a799061491f565b60405180910390fd5b732f8bc65e6587b4ac6408c92a66209a9f1ef2ce0863201add9b7f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59985876040518463ffffffff1660e01b8152600401612add9392919061484b565b60006040518083038186803b158015612af557600080fd5b505af4158015612b09573d6000803e3d6000fd5b50505050612b1684613d16565b612b55576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b4c90614a1f565b60405180910390fd5b612b5f838361395a565b612b7c30612b7760028461391090919063ffffffff16565b61395a565b505050600160068190555050565b612b92613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612c21576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c189061499f565b60405180910390fd5b60026006541415612c67576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c5e90614a3f565b60405180910390fd5b6002600681905550600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c632fc518b29091836040518363ffffffff1660e01b8152600401612cab929190614ac3565b60006040518083038186803b158015612cc357600080fd5b505af4158015612cd7573d6000803e3d6000fd5b50505050600160068190555050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600080612d7c86868686613dac565b9150915094509492505050565b600080600e60050154600e60060154915091509091565b612da8613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612e37576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e2e9061499f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612ea7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e9e906148df565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600560016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b7f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59981565b612f93613168565b73ffffffffffffffffffffffffffffffffffffffff16600560019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613022576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130199061499f565b60405180910390fd5b60026006541415613068576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161305f90614a3f565b60405180910390fd5b60026006819055506000600773ca54c7ae9a33a2a6f16eee603c97a2dd9ea6201c630eaced8190916040518263ffffffff1660e01b81526004016130ac9190614a7f565b60206040518083038186803b1580156130c457600080fd5b505af41580156130d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130fc9190614000565b90508073ffffffffffffffffffffffffffffffffffffffff167f2d38b612ec22e51eb40a71fe7d58e1a7ebb9989673619ae9f96cc0da907773d360405160405180910390a2506001600681905550565b60008061315b86868686613c74565b9150915094509492505050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156131e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016131d7906149ff565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415613250576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613247906148ff565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258360405161332e9190614c35565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156133ab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016133a2906149df565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561341b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613412906148bf565b60405180910390fd5b613426838383613e4e565b61349181604051806060016040528060268152602001614e22602691396000808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546135d09092919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550613524816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054613aee90919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516135c39190614c35565b60405180910390a3505050565b6000838311158290613618576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161360f919061489d565b60405180910390fd5b5060008385039050809150509392505050565b6000806000613677670de0b6b3a764000061366961365a87670de0b6b3a7640000613e5390919063ffffffff16565b8a613e9d90919063ffffffff16565b61391090919063ffffffff16565b90508481146136aa576136a5856136978884613e9d90919063ffffffff16565b61391090919063ffffffff16565b6136ac565b855b92506136c18188613e5390919063ffffffff16565b91505094509492505050565b6000600e73702281b181ffa845c48fd4729bdd7deca2e88589637f1dc4549091846040518363ffffffff1660e01b815260040161370b929190614bd5565b60206040518083038186803b15801561372357600080fd5b505af4158015613737573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061375b919061413f565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156137d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016137c9906149bf565b60405180910390fd5b6137de82600083613e4e565b61384981604051806060016040528060228152602001614e00602291396000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546135d09092919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506138a081600254613e5390919063ffffffff16565b600281905550600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516139049190614c35565b60405180910390a35050565b600061395283836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613f0d565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156139ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016139c190614a5f565b60405180910390fd5b6139d660008383613e4e565b6139eb81600254613aee90919063ffffffff16565b600281905550613a42816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054613aee90919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051613ae29190614c35565b60405180910390a35050565b600080828401905083811015613b39576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613b309061493f565b60405180910390fd5b8091505092915050565b6000806000858714613b7957613b7486613b66878a613e9d90919063ffffffff16565b61391090919063ffffffff16565b613b7b565b845b9050613bc4613b9b85670de0b6b3a7640000613e5390919063ffffffff16565b613bb6670de0b6b3a764000084613e9d90919063ffffffff16565b61391090919063ffffffff16565b9250613bd98184613e5390919063ffffffff16565b91505094509492505050565b732f8bc65e6587b4ac6408c92a66209a9f1ef2ce086312111b487f00000000000000000000000009e64c2b61a5f1690ee6fbed9baf5d6990f8dfd06000846040518463ffffffff1660e01b8152600401613c4193929190614814565b60006040518083038186803b158015613c5957600080fd5b505af4158015613c6d573d6000803e3d6000fd5b5050505050565b6000806000858514613caa57613ca586613c97878a613e9d90919063ffffffff16565b61391090919063ffffffff16565b613cac565b865b9050613cf5670de0b6b3a7640000613ce7613cd887670de0b6b3a7640000613e5390919063ffffffff16565b84613e9d90919063ffffffff16565b61391090919063ffffffff16565b9250613d0a8382613e5390919063ffffffff16565b91505094509492505050565b6000600e73702281b181ffa845c48fd4729bdd7deca2e88589637f1dc454909160006040518363ffffffff1660e01b8152600401613d55929190614bac565b60206040518083038186803b158015613d6d57600080fd5b505af4158015613d81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613da5919061413f565b9050919050565b6000806000613df8613dcf85670de0b6b3a7640000613e5390919063ffffffff16565b613dea670de0b6b3a76400008a613e9d90919063ffffffff16565b61391090919063ffffffff16565b9050848614613e2b57613e2685613e188884613e9d90919063ffffffff16565b61391090919063ffffffff16565b613e2d565b805b9250613e428782613e5390919063ffffffff16565b91505094509492505050565b505050565b6000613e9583836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506135d0565b905092915050565b600080831415613eb05760009050613f07565b6000828402905082848281613ec157fe5b0414613f02576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613ef99061497f565b60405180910390fd5b809150505b92915050565b60008083118290613f54576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613f4b919061489d565b60405180910390fd5b506000838581613f6057fe5b049050809150509392505050565b600081359050613f7d81614dba565b92915050565b600081519050613f9281614dba565b92915050565b600081519050613fa781614dd1565b92915050565b600081359050613fbc81614de8565b92915050565b600081519050613fd181614de8565b92915050565b600060208284031215613fe957600080fd5b6000613ff784828501613f6e565b91505092915050565b60006020828403121561401257600080fd5b600061402084828501613f83565b91505092915050565b6000806040838503121561403c57600080fd5b600061404a85828601613f6e565b925050602061405b85828601613f6e565b9150509250929050565b60008060006060848603121561407a57600080fd5b600061408886828701613f6e565b935050602061409986828701613f6e565b92505060406140aa86828701613fad565b9150509250925092565b600080604083850312156140c757600080fd5b60006140d585828601613f6e565b92505060206140e685828601613fad565b9150509250929050565b60008060006060848603121561410557600080fd5b600061411386828701613f83565b935050602061412486828701613fc2565b925050604061413586828701613fc2565b9150509250925092565b60006020828403121561415157600080fd5b600061415f84828501613f98565b91505092915050565b60006020828403121561417a57600080fd5b600061418884828501613fad565b91505092915050565b6000602082840312156141a357600080fd5b60006141b184828501613fc2565b91505092915050565b600080604083850312156141cd57600080fd5b60006141db85828601613fad565b92505060206141ec85828601613fad565b9150509250929050565b6000806040838503121561420957600080fd5b600061421785828601613fc2565b925050602061422885828601613fc2565b9150509250929050565b6000806000806080858703121561424857600080fd5b600061425687828801613fad565b945050602061426787828801613fad565b935050604061427887828801613fad565b925050606061428987828801613fad565b91505092959194509250565b61429e81614d2e565b82525050565b6142ad81614cd9565b82525050565b6142bc81614cd9565b82525050565b6142cb81614ceb565b82525050565b6142da81614d40565b82525050565b60006142eb82614cbd565b6142f58185614cc8565b9350614305818560208601614d76565b61430e81614da9565b840191505092915050565b6000614326602383614cc8565b91507f45524332303a207472616e7366657220746f20746865207a65726f206164647260008301527f65737300000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b600061438c602683614cc8565b91507f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008301527f64647265737300000000000000000000000000000000000000000000000000006020830152604082019050919050565b60006143f2602283614cc8565b91507f45524332303a20617070726f766520746f20746865207a65726f20616464726560008301527f73730000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000614458601d83614cc8565b91507f736861726573206d7573742062652067726561746572207468616e20300000006000830152602082019050919050565b6000614498601b83614cc8565b91507f536166654d6174683a206164646974696f6e206f766572666c6f7700000000006000830152602082019050919050565b60006144d8601b83614cc8565b91507f636f7374206d7573742062652067726561746572207468616e203000000000006000830152602082019050919050565b6000614518602183614cc8565b91507f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f60008301527f77000000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b600061457e602083614cc8565b91507f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726000830152602082019050919050565b60006145be602183614cc8565b91507f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008301527f73000000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000614624602583614cc8565b91507f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008301527f64726573730000000000000000000000000000000000000000000000000000006020830152604082019050919050565b600061468a602483614cc8565b91507f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008301527f72657373000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b60006146f0601b83614cc8565b91507f6e6f7420617661696c61626c6520617420746865206d6f6d656e7400000000006000830152602082019050919050565b6000614730601f83614cc8565b91507f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006000830152602082019050919050565b6000614770601f83614cc8565b91507f45524332303a206d696e7420746f20746865207a65726f2061646472657373006000830152602082019050919050565b8082525050565b8082525050565b6147ba81614d17565b82525050565b6147c981614d17565b82525050565b6147d881614d21565b82525050565b60006020820190506147f360008301846142b3565b92915050565b600060208201905061480e60008301846142a4565b92915050565b600060608201905061482960008301866142b3565b6148366020830185614295565b61484360408301846147c0565b949350505050565b600060608201905061486060008301866142b3565b61486d60208301856142b3565b61487a60408301846147c0565b949350505050565b600060208201905061489760008301846142c2565b92915050565b600060208201905081810360008301526148b781846142e0565b905092915050565b600060208201905081810360008301526148d881614319565b9050919050565b600060208201905081810360008301526148f88161437f565b9050919050565b60006020820190508181036000830152614918816143e5565b9050919050565b600060208201905081810360008301526149388161444b565b9050919050565b600060208201905081810360008301526149588161448b565b9050919050565b60006020820190508181036000830152614978816144cb565b9050919050565b600060208201905081810360008301526149988161450b565b9050919050565b600060208201905081810360008301526149b881614571565b9050919050565b600060208201905081810360008301526149d8816145b1565b9050919050565b600060208201905081810360008301526149f881614617565b9050919050565b60006020820190508181036000830152614a188161467d565b9050919050565b60006020820190508181036000830152614a38816146e3565b9050919050565b60006020820190508181036000830152614a5881614723565b9050919050565b60006020820190508181036000830152614a7881614763565b9050919050565b6000602082019050614a9460008301846147a3565b92915050565b6000604082019050614aaf60008301856147a3565b614abc60208301846142b3565b9392505050565b6000604082019050614ad860008301856147a3565b614ae560208301846147c0565b9392505050565b6000606082019050614b0160008301866147a3565b614b0e60208301856147c0565b614b1b60408301846147c0565b949350505050565b6000602082019050614b3860008301846147aa565b92915050565b6000604082019050614b5360008301856147aa565b614b6060208301846142b3565b9392505050565b6000608082019050614b7c60008301876147aa565b614b8960208301866142b3565b614b9660408301856142b3565b614ba360608301846147c0565b95945050505050565b6000604082019050614bc160008301856147aa565b614bce60208301846142d1565b9392505050565b6000604082019050614bea60008301856147aa565b614bf760208301846147c0565b9392505050565b6000606082019050614c1360008301866147aa565b614c2060208301856147c0565b614c2d60408301846147c0565b949350505050565b6000602082019050614c4a60008301846147b1565b92915050565b6000604082019050614c6560008301856147c0565b614c7260208301846147c0565b9392505050565b6000604082019050614c8e60008301856147b1565b614c9b60208301846147b1565b9392505050565b6000602082019050614cb760008301846147cf565b92915050565b600081519050919050565b600082825260208201905092915050565b6000614ce482614cf7565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b6000614d3982614d52565b9050919050565b6000614d4b82614d17565b9050919050565b6000614d5d82614d64565b9050919050565b6000614d6f82614cf7565b9050919050565b60005b83811015614d94578082015181840152602081019050614d79565b83811115614da3576000848401525b50505050565b6000601f19601f8301169050919050565b614dc381614cd9565b8114614dce57600080fd5b50565b614dda81614ceb565b8114614de557600080fd5b50565b614df181614d17565b8114614dfc57600080fd5b5056fe45524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220af02cf2834dd6c405bec5d33131a75a446b53c5e95d953bc3cef39f94eaf1f8f64736f6c634300060c0033

Deployed Bytecode Sourcemap

238487:124:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;87416:134;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;19762:83;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;21868:169;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;137126:140;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;140026:221;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;20837:100;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;22511:321;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;86958:122;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;90184:610;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;20689:83;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;23241:218;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;83399:304;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;134667:114;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;93360:317;;;:::i;:::-;;136000:129;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;137585:140;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;86424:115;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;85475:151;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;138068:296;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;91850:307;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;21000:119;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;2791:148;;;:::i;:::-;;88244:146;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;94361:227;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;86107:163;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;2149:79;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;135492:117;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;138848:725;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;19964:87;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;84318:311;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;80154:45;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;134972:103;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;23962:269;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;87788:143;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;21332:175;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;95657:444;;;:::i;:::-;;89047:534;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;92456:142;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;21570:151;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;82486:295;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;136584:204;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;3094:244;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;80203:46;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;94940:205;;;:::i;:::-;;81582:290;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;87416:134;87486:24;87526:3;:19;;;87519:26;;87416:134;:::o;19762:83::-;19799:13;19832:5;19825:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19762:83;:::o;21868:169::-;21951:4;21968:39;21977:12;:10;:12::i;:::-;21991:7;22000:6;21968:8;:39::i;:::-;22025:4;22018:11;;21868:169;;;;:::o;137126:140::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;137209:3:::2;:15;;;;137225:6;137209:23;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;137254:6;137242:19;;;;;;;;;;;;30266:1:::1;31228:7;:22;;;;137126:140:::0;:::o;140026:221::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;140168:3:::2;:23;;;;140192:22;140216:25;140168:74;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;30266:1:::1;31228:7;:22;;;;140026:221:::0;;:::o;20837:100::-;20890:7;20917:12;;20910:19;;20837:100;:::o;22511:321::-;22617:4;22634:36;22644:6;22652:9;22663:6;22634:9;:36::i;:::-;22681:121;22690:6;22698:12;:10;:12::i;:::-;22712:89;22750:6;22712:89;;;;;;;;;;;;;;;;;:11;:19;22724:6;22712:19;;;;;;;;;;;;;;;:33;22732:12;:10;:12::i;:::-;22712:33;;;;;;;;;;;;;;;;:37;;:89;;;;;:::i;:::-;22681:8;:121::i;:::-;22820:4;22813:11;;22511:321;;;;;:::o;86958:122::-;87024:20;87060:3;:15;;;87053:22;;86958:122;:::o;90184:610::-;30310:1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;90260:13:::1;90276:10;90260:26;;90314:1;90299:12;:16;90291:58;;;;;;;;;;;;:::i;:::-;;;;;;;;;90355:13;90370:18:::0;90392:101:::1;90432:12;90446:14;:12;:14::i;:::-;90462:13;:11;:13::i;:::-;90477:15;:13;:15::i;:::-;90392:39;:101::i;:::-;90354:139;;;;90514:1;90506:5;:9;90498:49;;;;;;;;;;;;:::i;:::-;;;;;;;;;90560:25;90579:5;90560:18;:25::i;:::-;90552:65;;;;;;;;;;;;:::i;:::-;;;;;;;;;90630:1;:5;90636;90643:1;:12;90656;90643:26;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;90630:40;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;90622:48;;90675:1;:11;90687:12;90701:5;90708;90675:39;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;90719:26;90725:5;90732:12;90719:5;:26::i;:::-;90750:39;90764:4;90771:17;90786:1;90771:10;:14;;:17;;;;:::i;:::-;90750:5;:39::i;:::-;31080:1;;;30266::::0;31228:7;:22;;;;90184:610;:::o;20689:83::-;20730:5;20755:9;;;;;;;;;;;20748:16;;20689:83;:::o;23241:218::-;23329:4;23346:83;23355:12;:10;:12::i;:::-;23369:7;23378:50;23417:10;23378:11;:25;23390:12;:10;:12::i;:::-;23378:25;;;;;;;;;;;;;;;:34;23404:7;23378:34;;;;;;;;;;;;;;;;:38;;:50;;;;:::i;:::-;23346:8;:83::i;:::-;23447:4;23440:11;;23241:218;;;;:::o;83399:304::-;83551:20;83573:18;83607:91;83647:5;83654:13;83669:12;83683:14;83607:39;:91::i;:::-;83600:98;;;;83399:304;;;;;;;:::o;134667:114::-;134721:21;134758:3;:16;;;;:18;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;134751:25;;134667:114;:::o;93360:317::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;93442:3:::2;:18;;;;:20;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;93468:21;93491::::0;93516:3:::2;:19;;;;:21;;;;;;;;;;;;;;;:::i;:::-;;::::0;::::2;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;93467:70;;;;93542:26;93554:13;93542:11;:26::i;:::-;93573:35;93587:4;93594:13;93573:5;:35::i;:::-;93618:54;93643:13;93658;93618:54;;;;;;;:::i;:::-;;;;;;;;31080:1;;30266::::1;31228:7;:22;;;;93360:317::o:0;136000:129::-;136068:16;136100:3;:16;;;;136117:6;136100:24;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;136093:31;;136000:129;;;:::o;137585:140::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;137668:3:::2;:15;;;;137684:6;137668:23;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;137713:6;137701:19;;;;;;;;;;;;30266:1:::1;31228:7;:22;;;;137585:140:::0;:::o;86424:115::-;86479:22;86517:3;:17;;;;;;;;;;;;86510:24;;86424:115;:::o;85475:151::-;85527:19;85560:3;:15;;;;:17;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:61;;79965:4;85560:61;;;80074:4;85560:61;85553:68;;85475:151;:::o;138068:296::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;138213:3:::2;:32;;;;138246:12;138260;138274:8;138213:70;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;138336:12;138293:66;;138322:12;138293:66;;;138350:8;138293:66;;;;;;:::i;:::-;;;;;;;;30266:1:::1;31228:7;:22;;;;138068:296:::0;;;:::o;91850:307::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;91973:13:::2;91989:10;91973:26;;92004:1;:11;92016;92029:5;92036:13;92004:46;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;92055;92065:5;92080:4;92087:13;92055:9;:46::i;:::-;92106:3;:16;;;;92123:13;92138;92106:46;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;31080:1;30266::::1;31228:7;:22;;;;91850:307:::0;;:::o;21000:119::-;21066:7;21093:9;:18;21103:7;21093:18;;;;;;;;;;;;;;;;21086:25;;21000:119;;;:::o;2791:148::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;2898:1:::1;2861:40;;2882:6;;;;;;;;;;;2861:40;;;;;;;;;;;;2929:1;2912:6;;:19;;;;;;;;;;;;;;;;;;2791:148::o:0;88244:146::-;88318:28;88362:3;:23;;;88355:30;;88244:146;:::o;94361:227::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;94476:3:::2;:25;;;;94502:19;94476:46;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;94563:19;94532:51;;;;;;;;;;;;30266:1:::1;31228:7;:22;;;;94361:227:::0;:::o;86107:163::-;86162:22;86198:3;:15;;;;:17;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:67;;80013:4;86198:67;;;80138:4;86198:67;86191:74;;86107:163;:::o;2149:79::-;2187:7;2214:6;;;;;;;;;;;2207:13;;2149:79;:::o;135492:117::-;135555:14;135585:3;:11;;;;135597:6;135585:19;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;135578:26;;135492:117;;;:::o;138848:725::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;138986:30:::2;139019:3;:16;;;;139036:12;139019:30;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;138986:63;;139054:30;139087:3;:16;;;;139104:12;139087:30;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;139054:63;;139122:3;:24;;;;139147:12;139161;139175:8;139122:62;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;139189:30;139222:3;:16;;;;139239:12;139222:30;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;139189:63;;139257:30;139290:3;:16;;;;139307:12;139290:30;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;139257:63;;139365:12;139330:58;;139351:12;139330:58;;;139379:8;139330:58;;;;;;:::i;:::-;;;;;;;;139417:12;139398:80;;;139431:22;139455;139398:80;;;;;;;:::i;:::-;;;;;;;;139507:12;139488:80;;;139521:22;139545;139488:80;;;;;;;:::i;:::-;;;;;;;;31080:1;;;;30266::::1;31228:7;:22;;;;138848:725:::0;;;:::o;19964:87::-;20003:13;20036:7;20029:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19964:87;:::o;84318:311::-;84477:13;84492:18;84526:98;84566:12;84580:13;84595:12;84609:14;84526:39;:98::i;:::-;84519:105;;;;84318:311;;;;;;;:::o;80154:45::-;;;:::o;134972:103::-;135024:14;135054:3;:14;;;;:16;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;135047:23;;134972:103;:::o;23962:269::-;24055:4;24072:129;24081:12;:10;:12::i;:::-;24095:7;24104:96;24143:15;24104:96;;;;;;;;;;;;;;;;;:11;:25;24116:12;:10;:12::i;:::-;24104:25;;;;;;;;;;;;;;;:34;24130:7;24104:34;;;;;;;;;;;;;;;;:38;;:96;;;;;:::i;:::-;24072:8;:129::i;:::-;24219:4;24212:11;;23962:269;;;;:::o;87788:143::-;87861:27;87904:3;:22;;;;;;;;;;;;87897:29;;87788:143;:::o;21332:175::-;21418:4;21435:42;21445:12;:10;:12::i;:::-;21459:9;21470:6;21435:9;:42::i;:::-;21495:4;21488:11;;21332:175;;;;:::o;95657:444::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;95745:3:::2;:18;;;;:20;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;95771:27;95800:21:::0;95823::::2;95848:3;:25;;;;:27;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;95770:105;;;;;;95880:1;:11;95892;95905:19;95926:13;95880:60;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;95945;95963:4;95970:19;95991:13;95945:9;:60::i;:::-;96046:19;96015:81;;;96067:13;96082;96015:81;;;;;;;:::i;:::-;;;;;;;;31080:1;;;30266::::1;31228:7;:22;;;;95657:444::o:0;89047:534::-;30310:1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;89115:13:::1;89131:10;89115:26;;89162:1;89154:5;:9;89146:49;;;;;;;;;;;;:::i;:::-;;;;;;;;;89201:18;89221::::0;89243:88:::1;89280:5;89287:14;:12;:14::i;:::-;89303:13;:11;:13::i;:::-;89318:12;:10;:12::i;:::-;89243:36;:88::i;:::-;89200:131;;;;89357:1;89344:10;:14;89336:56;;;;;;;;;;;;:::i;:::-;;;;;;;;;89397:1;:11;89409:12;89423:5;89430;89397:39;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;89449:22;89465:5;89449:15;:22::i;:::-;89441:62;;;;;;;;;;;;:::i;:::-;;;;;;;;;89508:24;89514:5;89521:10;89508:5;:24::i;:::-;89537:39;89551:4;89558:17;89573:1;89558:10;:14;;:17;;;;:::i;:::-;89537:5;:39::i;:::-;31080:1;;;30266::::0;31228:7;:22;;;;89047:534;:::o;92456:142::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;92561:3:::2;:18;;;;92580:12;92561:32;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;30266:1:::1;31228:7;:22;;;;92456:142:::0;:::o;21570:151::-;21659:7;21686:11;:18;21698:5;21686:18;;;;;;;;;;;;;;;:27;21705:7;21686:27;;;;;;;;;;;;;;;;21679:34;;21570:151;;;;:::o;82486:295::-;82637:13;82652:18;82686:90;82723:10;82735:13;82750:12;82764:11;82686:36;:90::i;:::-;82679:97;;;;82486:295;;;;;;;:::o;136584:204::-;136645:30;136677:33;136727:3;:25;;;136754:3;:28;;;136719:64;;;;136584:204;;:::o;3094:244::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;3203:1:::1;3183:22;;:8;:22;;;;3175:73;;;;;;;;;;;;:::i;:::-;;;;;;;;;3293:8;3264:38;;3285:6;;;;;;;;;;;3264:38;;;;;;;;;;;;3322:8;3313:6;;:17;;;;;;;;;;;;;;;;;;3094:244:::0;:::o;80203:46::-;;;:::o;94940:205::-;2371:12;:10;:12::i;:::-;2361:22;;:6;;;;;;;;;;;:22;;;2353:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1:::1;30916:7;;:19;;30908:63;;;;;;;;;;;;:::i;:::-;;;;;;;;;30310:1;31049:7;:18;;;;95026:27:::2;95056:3;:23;;;;:25;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;95026:55;;95120:19;95091:49;;;;;;;;;;;;31080:1;30266::::1;31228:7;:22;;;;94940:205::o:0;81582:290::-;81728:18;81748;81782:85;81819:5;81826:13;81841:12;81855:11;81782:36;:85::i;:::-;81775:92;;;;81582:290;;;;;;;:::o;703:106::-;756:15;791:10;784:17;;703:106;:::o;27107:346::-;27226:1;27209:19;;:5;:19;;;;27201:68;;;;;;;;;;;;:::i;:::-;;;;;;;;;27307:1;27288:21;;:7;:21;;;;27280:68;;;;;;;;;;;;:::i;:::-;;;;;;;;;27391:6;27361:11;:18;27373:5;27361:18;;;;;;;;;;;;;;;:27;27380:7;27361:27;;;;;;;;;;;;;;;:36;;;;27429:7;27413:32;;27422:5;27413:32;;;27438:6;27413:32;;;;;;:::i;:::-;;;;;;;;27107:346;;;:::o;24721:539::-;24845:1;24827:20;;:6;:20;;;;24819:70;;;;;;;;;;;;:::i;:::-;;;;;;;;;24929:1;24908:23;;:9;:23;;;;24900:71;;;;;;;;;;;;:::i;:::-;;;;;;;;;24984:47;25005:6;25013:9;25024:6;24984:20;:47::i;:::-;25064:71;25086:6;25064:71;;;;;;;;;;;;;;;;;:9;:17;25074:6;25064:17;;;;;;;;;;;;;;;;:21;;:71;;;;;:::i;:::-;25044:9;:17;25054:6;25044:17;;;;;;;;;;;;;;;:91;;;;25169:32;25194:6;25169:9;:20;25179:9;25169:20;;;;;;;;;;;;;;;;:24;;:32;;;;:::i;:::-;25146:9;:20;25156:9;25146:20;;;;;;;;;;;;;;;:55;;;;25234:9;25217:35;;25226:6;25217:35;;;25245:6;25217:35;;;;;;:::i;:::-;;;;;;;;24721:539;;;:::o;7972:192::-;8058:7;8091:1;8086;:6;;8094:12;8078:29;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;8118:9;8134:1;8130;:5;8118:17;;8155:1;8148:8;;;7972:192;;;;;:::o;36960:464::-;37113:13;37128:18;37155;37176:61;37232:4;37176:51;37193:33;37211:14;37201:4;37193:17;;:33;;;;:::i;:::-;37176:12;:16;;:51;;;;:::i;:::-;:55;;:61;;;;:::i;:::-;37155:82;;37264:12;37250:10;:26;:92;;37295:47;37329:12;37295:29;37310:13;37295:10;:14;;:29;;;;:::i;:::-;:33;;:47;;;;:::i;:::-;37250:92;;;37279:13;37250:92;37242:100;;37360:28;37377:10;37360:12;:16;;:28;;;;:::i;:::-;37347:41;;37393:26;36960:464;;;;;;;:::o;141659:128::-;141729:13;141758:3;:17;;;;141776:5;141758:24;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;141751:31;;141659:128;;;:::o;26251:418::-;26354:1;26335:21;;:7;:21;;;;26327:67;;;;;;;;;;;;:::i;:::-;;;;;;;;;26407:49;26428:7;26445:1;26449:6;26407:20;:49::i;:::-;26490:68;26513:6;26490:68;;;;;;;;;;;;;;;;;:9;:18;26500:7;26490:18;;;;;;;;;;;;;;;;:22;;:68;;;;;:::i;:::-;26469:9;:18;26479:7;26469:18;;;;;;;;;;;;;;;:89;;;;26584:24;26601:6;26584:12;;:16;;:24;;;;:::i;:::-;26569:12;:39;;;;26650:1;26624:37;;26633:7;26624:37;;;26654:6;26624:37;;;;;;:::i;:::-;;;;;;;;26251:418;;:::o;9370:132::-;9428:7;9455:39;9459:1;9462;9455:39;;;;;;;;;;;;;;;;;:3;:39::i;:::-;9448:46;;9370:132;;;;:::o;25541:378::-;25644:1;25625:21;;:7;:21;;;;25617:65;;;;;;;;;;;;:::i;:::-;;;;;;;;;25695:49;25724:1;25728:7;25737:6;25695:20;:49::i;:::-;25772:24;25789:6;25772:12;;:16;;:24;;;;:::i;:::-;25757:12;:39;;;;25828:30;25851:6;25828:9;:18;25838:7;25828:18;;;;;;;;;;;;;;;;:22;;:30;;;;:::i;:::-;25807:9;:18;25817:7;25807:18;;;;;;;;;;;;;;;:51;;;;25895:7;25874:37;;25891:1;25874:37;;;25904:6;25874:37;;;;;;:::i;:::-;;;;;;;;25541:378;;:::o;7069:181::-;7127:7;7147:9;7163:1;7159;:5;7147:17;;7188:1;7183;:6;;7175:46;;;;;;;;;;;;:::i;:::-;;;;;;;;;7241:1;7234:8;;;7069:181;;;;:::o;36285:466::-;36431:20;36453:18;36480;36510:13;36501:5;:22;:82;;36541:42;36569:13;36541:23;36551:12;36541:5;:9;;:23;;;;:::i;:::-;:27;;:42;;;;:::i;:::-;36501:82;;;36526:12;36501:82;36480:103;;36603:59;36628:33;36646:14;36636:4;36628:17;;:33;;;;:::i;:::-;36603:20;36618:4;36603:10;:14;;:20;;;;:::i;:::-;:24;;:59;;;;:::i;:::-;36588:74;;36680:28;36697:10;36680:12;:16;;:28;;;;:::i;:::-;36667:41;;36713:33;36285:466;;;;;;;:::o;97256:124::-;97324:1;:11;97336;97357:1;97361:13;97324:51;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;97256:124;:::o;34942:455::-;35082:18;35102;35129:20;35168:13;35152:12;:29;:82;;35192:42;35220:13;35192:23;35202:12;35192:5;:9;;:23;;;;:::i;:::-;:27;;:42;;;;:::i;:::-;35152:82;;;35184:5;35152:82;35129:105;;35252:58;35305:4;35252:48;35269:30;35287:11;35277:4;35269:17;;:30;;;;:::i;:::-;35252:12;:16;;:48;;;;:::i;:::-;:52;;:58;;;;:::i;:::-;35239:71;;35328:28;35345:10;35328:12;:16;;:28;;;;:::i;:::-;35315:41;;35361:31;34942:455;;;;;;;:::o;140857:152::-;140924:13;140984:3;:17;;;;141002:1;140984:20;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;140977:27;;140857:152;;;:::o;35611:457::-;35756:13;35771:18;35798:20;35821:56;35846:30;35864:11;35854:4;35846:17;;:30;;;;:::i;:::-;35821:20;35836:4;35821:10;:14;;:20;;;;:::i;:::-;:24;;:56;;;;:::i;:::-;35798:79;;35907:12;35890:13;:29;:96;;35937:49;35973:12;35937:31;35954:13;35937:12;:16;;:31;;;;:::i;:::-;:35;;:49;;;;:::i;:::-;35890:96;;;35922:12;35890:96;35882:104;;36004:28;36021:10;36004:12;:16;;:28;;;;:::i;:::-;35991:41;;36037:26;35611:457;;;;;;;:::o;28478:92::-;;;;:::o;7533:136::-;7591:7;7618:43;7622:1;7625;7618:43;;;;;;;;;;;;;;;;;:3;:43::i;:::-;7611:50;;7533:136;;;;:::o;8423:471::-;8481:7;8731:1;8726;:6;8722:47;;;8756:1;8749:8;;;;8722:47;8781:9;8797:1;8793;:5;8781:17;;8826:1;8821;8817;:5;;;;;;:10;8809:56;;;;;;;;;;;;:::i;:::-;;;;;;;;;8885:1;8878:8;;;8423:471;;;;;:::o;9998:278::-;10084:7;10116:1;10112;:5;10119:12;10104:28;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;10143:9;10159:1;10155;:5;;;;;;10143:17;;10267:1;10260:8;;;9998:278;;;;;:::o;5:130:-1:-;;85:6;72:20;63:29;;97:33;124:5;97:33;:::i;:::-;57:78;;;;:::o;142:134::-;;226:6;220:13;211:22;;238:33;265:5;238:33;:::i;:::-;205:71;;;;:::o;283:128::-;;364:6;358:13;349:22;;376:30;400:5;376:30;:::i;:::-;343:68;;;;:::o;418:130::-;;498:6;485:20;476:29;;510:33;537:5;510:33;:::i;:::-;470:78;;;;:::o;555:134::-;;639:6;633:13;624:22;;651:33;678:5;651:33;:::i;:::-;618:71;;;;:::o;696:241::-;;800:2;788:9;779:7;775:23;771:32;768:2;;;816:1;813;806:12;768:2;851:1;868:53;913:7;904:6;893:9;889:22;868:53;:::i;:::-;858:63;;830:97;762:175;;;;:::o;944:263::-;;1059:2;1047:9;1038:7;1034:23;1030:32;1027:2;;;1075:1;1072;1065:12;1027:2;1110:1;1127:64;1183:7;1174:6;1163:9;1159:22;1127:64;:::i;:::-;1117:74;;1089:108;1021:186;;;;:::o;1214:366::-;;;1335:2;1323:9;1314:7;1310:23;1306:32;1303:2;;;1351:1;1348;1341:12;1303:2;1386:1;1403:53;1448:7;1439:6;1428:9;1424:22;1403:53;:::i;:::-;1393:63;;1365:97;1493:2;1511:53;1556:7;1547:6;1536:9;1532:22;1511:53;:::i;:::-;1501:63;;1472:98;1297:283;;;;;:::o;1587:491::-;;;;1725:2;1713:9;1704:7;1700:23;1696:32;1693:2;;;1741:1;1738;1731:12;1693:2;1776:1;1793:53;1838:7;1829:6;1818:9;1814:22;1793:53;:::i;:::-;1783:63;;1755:97;1883:2;1901:53;1946:7;1937:6;1926:9;1922:22;1901:53;:::i;:::-;1891:63;;1862:98;1991:2;2009:53;2054:7;2045:6;2034:9;2030:22;2009:53;:::i;:::-;1999:63;;1970:98;1687:391;;;;;:::o;2085:366::-;;;2206:2;2194:9;2185:7;2181:23;2177:32;2174:2;;;2222:1;2219;2212:12;2174:2;2257:1;2274:53;2319:7;2310:6;2299:9;2295:22;2274:53;:::i;:::-;2264:63;;2236:97;2364:2;2382:53;2427:7;2418:6;2407:9;2403:22;2382:53;:::i;:::-;2372:63;;2343:98;2168:283;;;;;:::o;2458:535::-;;;;2607:2;2595:9;2586:7;2582:23;2578:32;2575:2;;;2623:1;2620;2613:12;2575:2;2658:1;2675:64;2731:7;2722:6;2711:9;2707:22;2675:64;:::i;:::-;2665:74;;2637:108;2776:2;2794:64;2850:7;2841:6;2830:9;2826:22;2794:64;:::i;:::-;2784:74;;2755:109;2895:2;2913:64;2969:7;2960:6;2949:9;2945:22;2913:64;:::i;:::-;2903:74;;2874:109;2569:424;;;;;:::o;3000:257::-;;3112:2;3100:9;3091:7;3087:23;3083:32;3080:2;;;3128:1;3125;3118:12;3080:2;3163:1;3180:61;3233:7;3224:6;3213:9;3209:22;3180:61;:::i;:::-;3170:71;;3142:105;3074:183;;;;:::o;3264:241::-;;3368:2;3356:9;3347:7;3343:23;3339:32;3336:2;;;3384:1;3381;3374:12;3336:2;3419:1;3436:53;3481:7;3472:6;3461:9;3457:22;3436:53;:::i;:::-;3426:63;;3398:97;3330:175;;;;:::o;3512:263::-;;3627:2;3615:9;3606:7;3602:23;3598:32;3595:2;;;3643:1;3640;3633:12;3595:2;3678:1;3695:64;3751:7;3742:6;3731:9;3727:22;3695:64;:::i;:::-;3685:74;;3657:108;3589:186;;;;:::o;3782:366::-;;;3903:2;3891:9;3882:7;3878:23;3874:32;3871:2;;;3919:1;3916;3909:12;3871:2;3954:1;3971:53;4016:7;4007:6;3996:9;3992:22;3971:53;:::i;:::-;3961:63;;3933:97;4061:2;4079:53;4124:7;4115:6;4104:9;4100:22;4079:53;:::i;:::-;4069:63;;4040:98;3865:283;;;;;:::o;4155:399::-;;;4287:2;4275:9;4266:7;4262:23;4258:32;4255:2;;;4303:1;4300;4293:12;4255:2;4338:1;4355:64;4411:7;4402:6;4391:9;4387:22;4355:64;:::i;:::-;4345:74;;4317:108;4456:2;4474:64;4530:7;4521:6;4510:9;4506:22;4474:64;:::i;:::-;4464:74;;4435:109;4249:305;;;;;:::o;4561:617::-;;;;;4716:3;4704:9;4695:7;4691:23;4687:33;4684:2;;;4733:1;4730;4723:12;4684:2;4768:1;4785:53;4830:7;4821:6;4810:9;4806:22;4785:53;:::i;:::-;4775:63;;4747:97;4875:2;4893:53;4938:7;4929:6;4918:9;4914:22;4893:53;:::i;:::-;4883:63;;4854:98;4983:2;5001:53;5046:7;5037:6;5026:9;5022:22;5001:53;:::i;:::-;4991:63;;4962:98;5091:2;5109:53;5154:7;5145:6;5134:9;5130:22;5109:53;:::i;:::-;5099:63;;5070:98;4678:500;;;;;;;:::o;5185:150::-;5284:45;5323:5;5284:45;:::i;:::-;5279:3;5272:58;5266:69;;:::o;5342:113::-;5425:24;5443:5;5425:24;:::i;:::-;5420:3;5413:37;5407:48;;:::o;5462:121::-;5553:24;5571:5;5553:24;:::i;:::-;5548:3;5541:37;5535:48;;:::o;5590:104::-;5667:21;5682:5;5667:21;:::i;:::-;5662:3;5655:34;5649:45;;:::o;5701:150::-;5800:45;5839:5;5800:45;:::i;:::-;5795:3;5788:58;5782:69;;:::o;5858:347::-;;5970:39;6003:5;5970:39;:::i;:::-;6021:71;6085:6;6080:3;6021:71;:::i;:::-;6014:78;;6097:52;6142:6;6137:3;6130:4;6123:5;6119:16;6097:52;:::i;:::-;6170:29;6192:6;6170:29;:::i;:::-;6165:3;6161:39;6154:46;;5950:255;;;;;:::o;6213:372::-;;6373:67;6437:2;6432:3;6373:67;:::i;:::-;6366:74;;6473:34;6469:1;6464:3;6460:11;6453:55;6542:5;6537:2;6532:3;6528:12;6521:27;6576:2;6571:3;6567:12;6560:19;;6359:226;;;:::o;6594:375::-;;6754:67;6818:2;6813:3;6754:67;:::i;:::-;6747:74;;6854:34;6850:1;6845:3;6841:11;6834:55;6923:8;6918:2;6913:3;6909:12;6902:30;6960:2;6955:3;6951:12;6944:19;;6740:229;;;:::o;6978:371::-;;7138:67;7202:2;7197:3;7138:67;:::i;:::-;7131:74;;7238:34;7234:1;7229:3;7225:11;7218:55;7307:4;7302:2;7297:3;7293:12;7286:26;7340:2;7335:3;7331:12;7324:19;;7124:225;;;:::o;7358:329::-;;7518:67;7582:2;7577:3;7518:67;:::i;:::-;7511:74;;7618:31;7614:1;7609:3;7605:11;7598:52;7678:2;7673:3;7669:12;7662:19;;7504:183;;;:::o;7696:327::-;;7856:67;7920:2;7915:3;7856:67;:::i;:::-;7849:74;;7956:29;7952:1;7947:3;7943:11;7936:50;8014:2;8009:3;8005:12;7998:19;;7842:181;;;:::o;8032:327::-;;8192:67;8256:2;8251:3;8192:67;:::i;:::-;8185:74;;8292:29;8288:1;8283:3;8279:11;8272:50;8350:2;8345:3;8341:12;8334:19;;8178:181;;;:::o;8368:370::-;;8528:67;8592:2;8587:3;8528:67;:::i;:::-;8521:74;;8628:34;8624:1;8619:3;8615:11;8608:55;8697:3;8692:2;8687:3;8683:12;8676:25;8729:2;8724:3;8720:12;8713:19;;8514:224;;;:::o;8747:332::-;;8907:67;8971:2;8966:3;8907:67;:::i;:::-;8900:74;;9007:34;9003:1;8998:3;8994:11;8987:55;9070:2;9065:3;9061:12;9054:19;;8893:186;;;:::o;9088:370::-;;9248:67;9312:2;9307:3;9248:67;:::i;:::-;9241:74;;9348:34;9344:1;9339:3;9335:11;9328:55;9417:3;9412:2;9407:3;9403:12;9396:25;9449:2;9444:3;9440:12;9433:19;;9234:224;;;:::o;9467:374::-;;9627:67;9691:2;9686:3;9627:67;:::i;:::-;9620:74;;9727:34;9723:1;9718:3;9714:11;9707:55;9796:7;9791:2;9786:3;9782:12;9775:29;9832:2;9827:3;9823:12;9816:19;;9613:228;;;:::o;9850:373::-;;10010:67;10074:2;10069:3;10010:67;:::i;:::-;10003:74;;10110:34;10106:1;10101:3;10097:11;10090:55;10179:6;10174:2;10169:3;10165:12;10158:28;10214:2;10209:3;10205:12;10198:19;;9996:227;;;:::o;10232:327::-;;10392:67;10456:2;10451:3;10392:67;:::i;:::-;10385:74;;10492:29;10488:1;10483:3;10479:11;10472:50;10550:2;10545:3;10541:12;10534:19;;10378:181;;;:::o;10568:331::-;;10728:67;10792:2;10787:3;10728:67;:::i;:::-;10721:74;;10828:33;10824:1;10819:3;10815:11;10808:54;10890:2;10885:3;10881:12;10874:19;;10714:185;;;:::o;10908:331::-;;11068:67;11132:2;11127:3;11068:67;:::i;:::-;11061:74;;11168:33;11164:1;11159:3;11155:11;11148:54;11230:2;11225:3;11221:12;11214:19;;11054:185;;;:::o;11247:125::-;11361:5;11356:3;11349:18;11343:29;;:::o;11379:125::-;11493:5;11488:3;11481:18;11475:29;;:::o;11511:113::-;11594:24;11612:5;11594:24;:::i;:::-;11589:3;11582:37;11576:48;;:::o;11631:121::-;11722:24;11740:5;11722:24;:::i;:::-;11717:3;11710:37;11704:48;;:::o;11759:107::-;11838:22;11854:5;11838:22;:::i;:::-;11833:3;11826:35;11820:46;;:::o;11873:238::-;;12008:2;11997:9;11993:18;11985:26;;12022:79;12098:1;12087:9;12083:17;12074:6;12022:79;:::i;:::-;11979:132;;;;:::o;12118:222::-;;12245:2;12234:9;12230:18;12222:26;;12259:71;12327:1;12316:9;12312:17;12303:6;12259:71;:::i;:::-;12216:124;;;;:::o;12347:492::-;;12546:2;12535:9;12531:18;12523:26;;12560:79;12636:1;12625:9;12621:17;12612:6;12560:79;:::i;:::-;12650:88;12734:2;12723:9;12719:18;12710:6;12650:88;:::i;:::-;12749:80;12825:2;12814:9;12810:18;12801:6;12749:80;:::i;:::-;12517:322;;;;;;:::o;12846:476::-;;13037:2;13026:9;13022:18;13014:26;;13051:79;13127:1;13116:9;13112:17;13103:6;13051:79;:::i;:::-;13141:80;13217:2;13206:9;13202:18;13193:6;13141:80;:::i;:::-;13232;13308:2;13297:9;13293:18;13284:6;13232:80;:::i;:::-;13008:314;;;;;;:::o;13329:210::-;;13450:2;13439:9;13435:18;13427:26;;13464:65;13526:1;13515:9;13511:17;13502:6;13464:65;:::i;:::-;13421:118;;;;:::o;13546:310::-;;13693:2;13682:9;13678:18;13670:26;;13743:9;13737:4;13733:20;13729:1;13718:9;13714:17;13707:47;13768:78;13841:4;13832:6;13768:78;:::i;:::-;13760:86;;13664:192;;;;:::o;13863:416::-;;14063:2;14052:9;14048:18;14040:26;;14113:9;14107:4;14103:20;14099:1;14088:9;14084:17;14077:47;14138:131;14264:4;14138:131;:::i;:::-;14130:139;;14034:245;;;:::o;14286:416::-;;14486:2;14475:9;14471:18;14463:26;;14536:9;14530:4;14526:20;14522:1;14511:9;14507:17;14500:47;14561:131;14687:4;14561:131;:::i;:::-;14553:139;;14457:245;;;:::o;14709:416::-;;14909:2;14898:9;14894:18;14886:26;;14959:9;14953:4;14949:20;14945:1;14934:9;14930:17;14923:47;14984:131;15110:4;14984:131;:::i;:::-;14976:139;;14880:245;;;:::o;15132:416::-;;15332:2;15321:9;15317:18;15309:26;;15382:9;15376:4;15372:20;15368:1;15357:9;15353:17;15346:47;15407:131;15533:4;15407:131;:::i;:::-;15399:139;;15303:245;;;:::o;15555:416::-;;15755:2;15744:9;15740:18;15732:26;;15805:9;15799:4;15795:20;15791:1;15780:9;15776:17;15769:47;15830:131;15956:4;15830:131;:::i;:::-;15822:139;;15726:245;;;:::o;15978:416::-;;16178:2;16167:9;16163:18;16155:26;;16228:9;16222:4;16218:20;16214:1;16203:9;16199:17;16192:47;16253:131;16379:4;16253:131;:::i;:::-;16245:139;;16149:245;;;:::o;16401:416::-;;16601:2;16590:9;16586:18;16578:26;;16651:9;16645:4;16641:20;16637:1;16626:9;16622:17;16615:47;16676:131;16802:4;16676:131;:::i;:::-;16668:139;;16572:245;;;:::o;16824:416::-;;17024:2;17013:9;17009:18;17001:26;;17074:9;17068:4;17064:20;17060:1;17049:9;17045:17;17038:47;17099:131;17225:4;17099:131;:::i;:::-;17091:139;;16995:245;;;:::o;17247:416::-;;17447:2;17436:9;17432:18;17424:26;;17497:9;17491:4;17487:20;17483:1;17472:9;17468:17;17461:47;17522:131;17648:4;17522:131;:::i;:::-;17514:139;;17418:245;;;:::o;17670:416::-;;17870:2;17859:9;17855:18;17847:26;;17920:9;17914:4;17910:20;17906:1;17895:9;17891:17;17884:47;17945:131;18071:4;17945:131;:::i;:::-;17937:139;;17841:245;;;:::o;18093:416::-;;18293:2;18282:9;18278:18;18270:26;;18343:9;18337:4;18333:20;18329:1;18318:9;18314:17;18307:47;18368:131;18494:4;18368:131;:::i;:::-;18360:139;;18264:245;;;:::o;18516:416::-;;18716:2;18705:9;18701:18;18693:26;;18766:9;18760:4;18756:20;18752:1;18741:9;18737:17;18730:47;18791:131;18917:4;18791:131;:::i;:::-;18783:139;;18687:245;;;:::o;18939:416::-;;19139:2;19128:9;19124:18;19116:26;;19189:9;19183:4;19179:20;19175:1;19164:9;19160:17;19153:47;19214:131;19340:4;19214:131;:::i;:::-;19206:139;;19110:245;;;:::o;19362:416::-;;19562:2;19551:9;19547:18;19539:26;;19612:9;19606:4;19602:20;19598:1;19587:9;19583:17;19576:47;19637:131;19763:4;19637:131;:::i;:::-;19629:139;;19533:245;;;:::o;19785:284::-;;19943:2;19932:9;19928:18;19920:26;;19957:102;20056:1;20045:9;20041:17;20032:6;19957:102;:::i;:::-;19914:155;;;;:::o;20076:403::-;;20262:2;20251:9;20247:18;20239:26;;20276:102;20375:1;20364:9;20360:17;20351:6;20276:102;:::i;:::-;20389:80;20465:2;20454:9;20450:18;20441:6;20389:80;:::i;:::-;20233:246;;;;;:::o;20486:403::-;;20672:2;20661:9;20657:18;20649:26;;20686:102;20785:1;20774:9;20770:17;20761:6;20686:102;:::i;:::-;20799:80;20875:2;20864:9;20860:18;20851:6;20799:80;:::i;:::-;20643:246;;;;;:::o;20896:522::-;;21110:2;21099:9;21095:18;21087:26;;21124:102;21223:1;21212:9;21208:17;21199:6;21124:102;:::i;:::-;21237:80;21313:2;21302:9;21298:18;21289:6;21237:80;:::i;:::-;21328;21404:2;21393:9;21389:18;21380:6;21328:80;:::i;:::-;21081:337;;;;;;:::o;21425:284::-;;21583:2;21572:9;21568:18;21560:26;;21597:102;21696:1;21685:9;21681:17;21672:6;21597:102;:::i;:::-;21554:155;;;;:::o;21716:403::-;;21902:2;21891:9;21887:18;21879:26;;21916:102;22015:1;22004:9;22000:17;21991:6;21916:102;:::i;:::-;22029:80;22105:2;22094:9;22090:18;22081:6;22029:80;:::i;:::-;21873:246;;;;;:::o;22126:642::-;;22368:3;22357:9;22353:19;22345:27;;22383:102;22482:1;22471:9;22467:17;22458:6;22383:102;:::i;:::-;22496:80;22572:2;22561:9;22557:18;22548:6;22496:80;:::i;:::-;22587;22663:2;22652:9;22648:18;22639:6;22587:80;:::i;:::-;22678;22754:2;22743:9;22739:18;22730:6;22678:80;:::i;:::-;22339:429;;;;;;;:::o;22775:419::-;;22969:2;22958:9;22954:18;22946:26;;22983:102;23082:1;23071:9;23067:17;23058:6;22983:102;:::i;:::-;23096:88;23180:2;23169:9;23165:18;23156:6;23096:88;:::i;:::-;22940:254;;;;;:::o;23201:403::-;;23387:2;23376:9;23372:18;23364:26;;23401:102;23500:1;23489:9;23485:17;23476:6;23401:102;:::i;:::-;23514:80;23590:2;23579:9;23575:18;23566:6;23514:80;:::i;:::-;23358:246;;;;;:::o;23611:522::-;;23825:2;23814:9;23810:18;23802:26;;23839:102;23938:1;23927:9;23923:17;23914:6;23839:102;:::i;:::-;23952:80;24028:2;24017:9;24013:18;24004:6;23952:80;:::i;:::-;24043;24119:2;24108:9;24104:18;24095:6;24043:80;:::i;:::-;23796:337;;;;;;:::o;24140:222::-;;24267:2;24256:9;24252:18;24244:26;;24281:71;24349:1;24338:9;24334:17;24325:6;24281:71;:::i;:::-;24238:124;;;;:::o;24369:357::-;;24532:2;24521:9;24517:18;24509:26;;24546:79;24622:1;24611:9;24607:17;24598:6;24546:79;:::i;:::-;24636:80;24712:2;24701:9;24697:18;24688:6;24636:80;:::i;:::-;24503:223;;;;;:::o;24733:333::-;;24888:2;24877:9;24873:18;24865:26;;24902:71;24970:1;24959:9;24955:17;24946:6;24902:71;:::i;:::-;24984:72;25052:2;25041:9;25037:18;25028:6;24984:72;:::i;:::-;24859:207;;;;;:::o;25073:214::-;;25196:2;25185:9;25181:18;25173:26;;25210:67;25274:1;25263:9;25259:17;25250:6;25210:67;:::i;:::-;25167:120;;;;:::o;25294:122::-;;25388:5;25382:12;25372:22;;25353:63;;;:::o;25424:163::-;;25539:6;25534:3;25527:19;25576:4;25571:3;25567:14;25552:29;;25520:67;;;;:::o;25595:91::-;;25657:24;25675:5;25657:24;:::i;:::-;25646:35;;25640:46;;;:::o;25693:85::-;;25766:5;25759:13;25752:21;25741:32;;25735:43;;;:::o;25785:121::-;;25858:42;25851:5;25847:54;25836:65;;25830:76;;;:::o;25913:72::-;;25975:5;25964:16;;25958:27;;;:::o;25992:81::-;;26063:4;26056:5;26052:16;26041:27;;26035:38;;;:::o;26080:129::-;;26167:37;26198:5;26167:37;:::i;:::-;26154:50;;26148:61;;;:::o;26216:116::-;;26303:24;26321:5;26303:24;:::i;:::-;26290:37;;26284:48;;;:::o;26339:121::-;;26418:37;26449:5;26418:37;:::i;:::-;26405:50;;26399:61;;;:::o;26467:108::-;;26546:24;26564:5;26546:24;:::i;:::-;26533:37;;26527:48;;;:::o;26583:268::-;26648:1;26655:101;26669:6;26666:1;26663:13;26655:101;;;26745:1;26740:3;26736:11;26730:18;26726:1;26721:3;26717:11;26710:39;26691:2;26688:1;26684:10;26679:15;;26655:101;;;26771:6;26768:1;26765:13;26762:2;;;26836:1;26827:6;26822:3;26818:16;26811:27;26762:2;26632:219;;;;:::o;26859:97::-;;26947:2;26943:7;26938:2;26931:5;26927:14;26923:28;26913:38;;26907:49;;;:::o;26964:117::-;27033:24;27051:5;27033:24;:::i;:::-;27026:5;27023:35;27013:2;;27072:1;27069;27062:12;27013:2;27007:74;:::o;27088:111::-;27154:21;27169:5;27154:21;:::i;:::-;27147:5;27144:32;27134:2;;27190:1;27187;27180:12;27134:2;27128:71;:::o;27206:117::-;27275:24;27293:5;27275:24;:::i;:::-;27268:5;27265:35;27255:2;;27314:1;27311;27304:12;27255:2;27249:74;:::o

Swarm Source

ipfs://af02cf2834dd6c405bec5d33131a75a446b53c5e95d953bc3cef39f94eaf1f8f

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  ]

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.